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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions