benruehl / adonis-ui

Lightweight UI toolkit for WPF applications offering classic but enhanced windows visuals
https://benruehl.github.io/adonis-ui/
MIT License
1.69k stars 143 forks source link

Memory leak to AdonisWindow from HandleTitleBarActualHeightChanged #207

Open hypehuman opened 4 months ago

hypehuman commented 4 months ago

Describe the bug AdonisWindow is not being removed from memory after the window is closed. The window, its content, and its DataContext (i.e., business logic) is therefore not getting garbage collected.

To Reproduce Steps to reproduce the behavior:

  1. Build and run the following WPF application: AdonisLeakTest.zip
  2. Click on 'Launch Simulation' (this opens a new AdonisWindow).
  3. Close the new window, but keep main window open.
  4. Force or wait for garbage collection.
  5. BUG: The closed AdonisWindow, its Content (of type SimulationView) and the its Content.DataContext (of type SimulationVM) still exist in memory.

Expected behavior After closing the window, the AdonisWindow, the SimulationView, and the SimulationVM should have been eligible for garbage collection. However, there is a memory leak keeping them alive. From the DotMemory screenshots below, it looks like the culprit is a DependencyObjectPropertyDescriptor for the ActualHeight property. This descriptor was added in AdonisWindow.HandleTitleBarActualHeightChanged.

The method uses DependencyPropertyDescriptor.AddValueChanged. I think it needs to either use a weak event, or remove the handler at some point.

Screenshots These screenshots are from JetBrains DotMemory, from a snapshot taken after doing "To Reproduce" and then forcing garbage collection.

Here you can see that SimulationVM is still in memory, and that AdonisWindow is keeping it alive. The path contains an EventHandler and a DependencyObjectPropertyDescriptor.

leak path

Here is the stack trace for the creation of the EventHandler in the previous screenshot. It shows that the event handler was created from AdonisWindow.HandleTitleBarActualHeightChanged while the window was opening.

leak stack trace

Finally, here are some properties of the aforementioned DependencyObjectPropertyDescriptor, confirming that it's related to "ActualHeight".

leak properties