Note that there are some explanatory texts on larger screens.

plurals
  1. POHow can one free memory used by heavy WPF Controls in a deterministic way?
    primarykey
    data
    text
    <p>I have been running into issues when Controls allocating big amounts of memory get destroyed and reconstructed using Input events from the UI. </p> <p>Obviously, setting a Window's Content to null is not enough to free the memory the contained Control was using. It will be GCed eventually. But as Input events to destroy and construct Controls get more frequent, the GC seems to skip some objects.</p> <p>I broke it down to this example:</p> <p>XAML:</p> <pre><code>&lt;Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" x:Name="window" MouseMove="window_MouseMove"&gt; &lt;/Window&gt; </code></pre> <p>C#:</p> <pre><code>using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace WpfApplication1 { public partial class MainWindow : Window { private class HeavyObject : UserControl { private byte[] x = new byte[750000000]; public HeavyObject() { for (int i = 0; i++ &lt; 99999999; ) { } // just so we can follow visually in Process Explorer Content = "Peekaboo!"; // change Content to el cheapo re-trigger MouseMove } } public MainWindow() { InitializeComponent(); //Works 1: //while (true) //{ // window.Content = null; // window.Content = new HeavyObject(); //} } private void window_MouseMove(object sender, MouseEventArgs e) { if (window.Content == null) { GC.Collect(); GC.WaitForPendingFinalizers(); // Works 2: //new HeavyObject(); //window.Content = "Peekaboo!"; // change Content to el cheapo re-trigger MouseMove //return; window.Content = new HeavyObject(); } else { window.Content = null; } } } } </code></pre> <p>This does allocate a ~750MB object (HeavyObject) at each MouseMove event and put it as the main Window's Content. If the Cursor is kept still on the Window the Content change will endlessly re-trigger the event. The Reference to HeavyObject is nulled before its next construction and a futile attempt to GC its remains is triggered.</p> <p>What we can see in ProcessExplorer / Taskman is, that there is sometimes 1.5GB or more allocated. If not, move the mouse wildly and leave and re-enter the window. If you compile for x86 without LARGEADDRESSAWARE, you'll get an OutOfMemoryException instead (limit ~1.3GB).</p> <p>You won't encounter this behavior if you either allocate the HeavyObject in an endless loop without using mouse input (uncomment section "Works 1") or if you trigger the allocation by mouse input but do not put the object into the visual tree (uncomment seciton "Works 2"). </p> <p>So i suppose it has something to do with the visual tree lazily freeing resources. But then there is another strange effect: If 1.5GB are consumed during the cursor is outside the Window, it seems the GC won't kick in until MouseMove is triggered again. At least, memory consumption seems to stabilize as long as no further events are triggered. So either there is a long-living reference left somewhere or GC gets lazy when there is no activity.</p> <p>Seems strange enough to me. Can you figure out what's going on?</p> <p><strong>Edit:</strong> As BalamBalam commented: The data behind the Control could be dereferenced before destroying the Control. That would have been plan B. Nevertheless maybe there is a more generic solution.</p> <p>Saying such Controls are bad code is not helpful. I would like to know why an object I hold no reference to gets GCed if I leave the UI alone for a few Ticks after dereferencing it, but lingers around forever if another one (that has to have been created by user input) immediately takes its place.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload