Skip to content

[desktop_multi_window] Handle leak (~3/cycle) when a sub-window is repeatedly destroyed and recreated #484

Description

@wonpyoJang

Summary

When a desktop_multi_window sub-window is destroyed (e.g. it receives WM_CLOSE, or dies for any reason) and then recreated, the host process leaks ~3 Windows handles per recreate cycle (linear). There is no public API to destroy a sub-window (only show()/hide()), and the engine resources are not fully reclaimed when the window closes.

Environment

  • Flutter 3.44.1 (stable), Windows 10 x64
  • desktop_multi_window: 0.3.0

Observed (measured)

Windows handle count via GetProcessHandleCount, across forced-destroy + auto-recreate cycles (PostMessage WM_CLOSE to the sub-window HWND, wait for the host app to recreate it):

cycles handles WorkingSet GDI USER
baseline 674 114.7 MB 25 39
after 15 720 (+46, ~3/cycle linear) 127.2 MB (+12.5) 25 (0) 40 (+1)
  • Recreation succeeds 15/15, process never crashes.
  • Normal operation (window kept alive, no recreation) shows no leak — handles stable (675 → 672) over 80s. So the leak is strictly tied to the destroy→recreate transition.

Analysis (from the 0.3.0 Windows source)

  • WindowController (Dart) exposes only show() / hide() — there is no close() / destroy().
  • The main channel mixin.one/desktop_multi_window handles only createWindow / getWindowDefinition / getAllWindows.
  • FlutterWindow::OnDestroy() sets flutter_controller_ = nullptr (destroying the FlutterViewController) and calls RemoveManagedFlutterWindowLater(id). The managed FlutterWindow is only erased later via CleanupRemovedWindows(), which is invoked solely on the next Create().
  • Even with that cleanup running, ~3 handles/cycle are not reclaimed — suggesting FlutterViewController/engine teardown on Windows does not fully release its thread/event handles.

Reproduction

  1. WindowController.create(...) a sub-window.
  2. Force-close it: PostMessage(hwnd, WM_CLOSE, 0, 0) to the sub-window's top-level HWND.
  3. Recreate via WindowController.create(...).
  4. Repeat. Watch GetProcessHandleCount(process) climb ~3 per cycle.

Impact

  • Typical apps: negligible — sub-windows rarely die, so they're rarely recreated.
  • Apps that auto-recreate a sub-window for resilience (e.g. an always-on-top overlay that respawns if it disappears) can accumulate handles over time if the window dies repeatedly.

Suggestions

  1. Add an explicit WindowController.close() (Dart) + closeWindow native handler that destroys the sub-window and immediately tears down its FlutterViewController and erases it from managed_flutter_windows_, instead of deferring to the next Create().
  2. Investigate whether FlutterViewController teardown itself leaks handles on Windows — if so, an upstream flutter/flutter follow-up may be warranted.

Happy to provide the full measurement harness (Win32 EnumWindows/GetProcessHandleCount/GetGuiResources) if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions