none
DynamicResource\StaticResource cause memory leaks

    Question

  • I have noticed that using DynamicResourceExtension\StaticResourceExtension prevents my page instances from being Garbage Collected while they are referencing Application level resources.

    I have a simple app. The class Window1 creates an instance on Page1 and stores it in a field. Page1 references a SolidColorBrush defined in the Application level resource App.xaml. When the Button named "ReleaseButton" is clicked on Window1, the field reference to Page1 is set to null and GC.Collect() invoked. Page1 never gets garbage collected even though I have released all my explicit references to the object. Removing the DynamicResource extension however allows the Page1 instance to be GC'd as is evident by the call to Trace.TraceInformation whose output can be seen in the output window when Page1 is finalized.

    Are there any known workarounds?

    App.xaml

    <Application x:Class="WeakReferences.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        StartupUri="Window1.xaml">
        <Application.Resources>
            <SolidColorBrush x:Key="MyBrush" Color="SkyBlue"/>
        </Application.Resources>
    </Application>

    Window1.xaml

    <Window x:Class="WeakReferences.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <Button Name="ReleaseButton" Content="Release Reference" Click="Button_Click"/>
        </Grid>
    </Window>

    Window1.xaml.cs

    public partial class Window1 : Window
        {
            object p;

            public Window1()
            {
                InitializeComponent();

                p = new Page1();
            }

            private void Button_Click(object sender, RoutedEventArgs e)
            {            
                p = null;

                GC.Collect();
            }
        }

    Page1.xaml

    <Page x:Class="WeakReferences.Page1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Page1"
          Background="{DynamicResource MyBrush}">
        <Grid>
        </Grid>
    </Page>

    Page1.xaml.cs

    public partial class Page1 : Page
        {
            public Page1()
            {
                InitializeComponent();
            }

            ~Page1()
            {
                Trace.TraceInformation("Page1 Finalized.");
            }
        }

    Monday, September 08, 2008 9:13 AM

Answers

  • Entries in a resource dictionary are lazily hydrated. To do this, the ResourceDictionary hands out a DeferredReference to refer to an item in the dictionary. When that item is hydrated that deferred reference raises an event - Inflated. The problem is that the ResourceReferenceExpression is hooked into that event and never unhooks (or maybe its should use a weakeventlistener to listen for the events). In any case, you can get around this by forcing the items in the dictionary to be hydrated before anyone tries to reference this. One way to do this is to just iterate the resources in the OnStartup of the Application.
    e.g.
        public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                WalkDictionary(this.Resources);
     
                base.OnStartup(e);
            }
     
            private static void WalkDictionary(ResourceDictionary resources)
            {
                foreach (DictionaryEntry entry in resources)
                {
                }
     
                foreach (ResourceDictionary rd in resources.MergedDictionaries)
                    WalkDictionary(rd);
            }
        }
    • Proposed as answer by James Miles Monday, February 09, 2009 4:31 AM
    • Marked as answer by Marco Zhou Tuesday, March 10, 2009 5:28 AM
    Thursday, October 09, 2008 12:25 PM
  • I can verify this, unfortunately, I cannot find any workaround except setting the DynamicResource or StaticResource referencing at Loaded event, and clear them out at the Unloaded time.

    I would greatly appreciate it if you could file a bug on this at the connect site:

    https://connect.microsoft.com/feedback/default.aspx?SiteID=212&wa=wsignin1.0


    Thanks
    • Marked as answer by Marco Zhou Friday, September 12, 2008 9:57 AM
    Wednesday, September 10, 2008 9:36 AM

All replies

  • I can verify this, unfortunately, I cannot find any workaround except setting the DynamicResource or StaticResource referencing at Loaded event, and clear them out at the Unloaded time.

    I would greatly appreciate it if you could file a bug on this at the connect site:

    https://connect.microsoft.com/feedback/default.aspx?SiteID=212&wa=wsignin1.0


    Thanks
    • Marked as answer by Marco Zhou Friday, September 12, 2008 9:57 AM
    Wednesday, September 10, 2008 9:36 AM
  • While possbile, that approach is infeasible for my scenario. I have a relatively large WPF app with well over 1000  Static\DynamicResource references; replacing all/most of the MarkupExtensions with arbitrary code would be a hassle.

    Also, I visited the Connect website and for the past hour have found no way to submit a report.

    I appreciate your input.
    Wednesday, September 10, 2008 11:03 PM
  • You need to register on the Connect site before filing a bug, I think the live email account should be enough to log on to that site.

    I am still in the process of thinking of any possible workaround:)

    Thanks
    Thursday, September 11, 2008 3:37 AM
  • Entries in a resource dictionary are lazily hydrated. To do this, the ResourceDictionary hands out a DeferredReference to refer to an item in the dictionary. When that item is hydrated that deferred reference raises an event - Inflated. The problem is that the ResourceReferenceExpression is hooked into that event and never unhooks (or maybe its should use a weakeventlistener to listen for the events). In any case, you can get around this by forcing the items in the dictionary to be hydrated before anyone tries to reference this. One way to do this is to just iterate the resources in the OnStartup of the Application.
    e.g.
        public partial class App : Application
        {
            protected override void OnStartup(StartupEventArgs e)
            {
                WalkDictionary(this.Resources);
     
                base.OnStartup(e);
            }
     
            private static void WalkDictionary(ResourceDictionary resources)
            {
                foreach (DictionaryEntry entry in resources)
                {
                }
     
                foreach (ResourceDictionary rd in resources.MergedDictionaries)
                    WalkDictionary(rd);
            }
        }
    • Proposed as answer by James Miles Monday, February 09, 2009 4:31 AM
    • Marked as answer by Marco Zhou Tuesday, March 10, 2009 5:28 AM
    Thursday, October 09, 2008 12:25 PM
  • We're debugging memory leaks in our application and ran into this post. My first thought, was, "No way. This can't be the case", and quickly sat down and built a sample according to what Brian posted.

    Sure enough Page1 wasn't getting garbage collected.

    Then, I modified the sample somewhat (because I wanted a button to call Collect on multiple times) ... and tada ... Page1 was getting garbage collected.

    I have no idea why and am curious what others think. (I admit I don't fully understand Andrew's response and how it applies to Brian's example ... maybe Andrew's response explains my situation as well.)

    Here is the code ... I will only post the code that is different than the above sample ... which is basically the code and xaml for Window1.

    Window1.xaml:
    1<Window 
    2    x:Class="MemoryLeaksViaResources.Window1" 
    3    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    4    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    5    Title="Window1" 
    6    Height="300" 
    7    Width="300" 
    8    Initialized="OnInitialized" 
    9> 
    10    <Grid> 
    11        <Frame x:Name="frame"/> 
    12        <StackPanel 
    13            HorizontalAlignment="Center" 
    14            VerticalAlignment="Center" 
    15        > 
    16            <Button 
    17                Content="Remove Page" 
    18                Click="removeButton_Click" 
    19                Padding="10" 
    20            /> 
    21            <Button 
    22                Content="GC.Collect" 
    23                Padding="10" 
    24                Click="collectButton_Click" 
    25            /> 
    26        </StackPanel> 
    27    </Grid> 
    28</Window> 
    29 

    Window1.xaml.cs:
    1namespace MemoryLeaksViaResources 
    2
    3    /// <summary> 
    4    /// Interaction logic for Window1.xaml 
    5    /// </summary> 
    6    public partial class Window1 : Window 
    7    { 
    8        public Window1() 
    9        { 
    10            InitializeComponent(); 
    11        } 
    12 
    13        private Page page; 
    14 
    15        private void OnInitialized(object sender, EventArgs e) 
    16        { 
    17            this.frame.Source = new Uri("pack://application:,,,/Page1.xaml"); 
    18        } 
    19 
    20        private void removeButton_Click(object sender, RoutedEventArgs e) 
    21        { 
    22            this.frame.ClearValue(Frame.SourceProperty); 
    23        } 
    24 
    25        private void collectButton_Click(object sender, RoutedEventArgs e) 
    26        { 
    27            GC.Collect(); 
    28        } 
    29    } 
    30
    31 

    • Edited by Cory Plotts Sunday, March 08, 2009 1:10 AM Simple grammar mistake
    Sunday, March 08, 2009 12:49 AM
  • Hey, been doing some debugging through the .NET Framework source code on this matter ... and I think the leak is specific to DynamicResource(s).

    Please correct me if you think I am wrong ... but here is why ...

    DynamicResourceExtension uses ResourceReferenceExpression ... which is (as Andrew states above) the thing that listens to the Inflated event (and the cause of these leaks).

    However, StaticResourceExtension does not use ResourceReferenceExpression at all. Thus, no leaks. If the static resource is deferring the reference ... it uses DeferredResourceReference directly (but it never attaches to the Inflated event).

    Given all this ... I am very surprised that this isn't a major, big-time issue. I would think that a lot of applications use DynamicResource(s) to application level resources. And, I would be surprised if those applications are all walking the resource dictionaries (taking advantage of Andrew's suggestion) to inflate all of these type of resources.


    Monday, March 09, 2009 6:12 PM
  • Has anyone posted this on connect?  If so, please post the link to the bug report.
    Monday, March 09, 2009 6:26 PM
  • So, I have figured out why the code I posted above doesn't leak.

    When you add Page1 to the visual tree:
    private void OnInitialized(object sender, EventArgs e)  
    {  
        this.frame.Source = new Uri("pack://application:,,,/Page1.xaml");  

    As part of the measure, arrange, render cycle, it inflates the resource ... at which point the ResourceReferenceExpression detaches from the Inflated event ... and thus no leak.

    What I understand this to mean, is that if the item that is referencing the application level resource ... through a DynamicResource ... actually becomes part of a rendered visual tree ... this leak does not occur.

    This got me thinking ... what about the situation where a DynamicResource is being used as part of a Trigger, say, the IsPressed trigger on a button? This resource wouldn't get used unless the user clicked the button, right?

    So, here is modified xaml in Page1.xaml where I test that out (there is obviously a new brush in the application resources as well with the key name of 'pressedBrush'):
    1<Page 
    2    x:Class="MemoryLeaksViaResources.Page1" 
    3    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    4    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    5    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    6    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    7    mc:Ignorable="d" 
    8    d:DesignWidth="300" 
    9    d:DesignHeight="300" 
    10    Title="Page1" 
    11> 
    12    <!--Background="{DynamicResource myBrush}"--> 
    13    <Grid> 
    14        <Grid.RowDefinitions> 
    15            <RowDefinition Height=".333*"/> 
    16            <RowDefinition Height=".333*"/> 
    17            <RowDefinition Height=".333*"/> 
    18        </Grid.RowDefinitions> 
    19        <Button Grid.Row="2" Content="Press to Inflate pressedBrush" Foreground="White" TextElement.FontSize="16"
    20            <Button.Template> 
    21                <ControlTemplate TargetType="{x:Type Button}"
    22                    <Border Background="Black" x:Name="border"
    23                        <ContentPresenter 
    24                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
    25                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
    26                        /> 
    27                    </Border> 
    28                    <ControlTemplate.Triggers> 
    29                        <Trigger Property="IsPressed" Value="True"
    30                            <Setter Property="Background" TargetName="border" Value="{DynamicResource pressedBrush}"/> 
    31                        </Trigger> 
    32                    </ControlTemplate.Triggers> 
    33                </ControlTemplate> 
    34            </Button.Template> 
    35        </Button> 
    36    </Grid> 
    37</Page> 
    38 

    This did not leak as well. Why? Well, when the trigger invalidates the property ... it just fetches the resource directly from the DeferredResourceReference ... that is ... it does not use a ResourceReferenceExpression (the cause of the leak).

    Marco Zhou? Do you know more? Can you clarify exactly what situations that using a DynamicResource to reference an application level resource ... can cause a leak ... and which do not? This bug makes me want to avoid using DynamicResources ... which pains me.

    Has a bug been posted on the Connect site? I would be happy to post it, if not. Just let me know.

    Thanks,
    Cory
    Monday, March 09, 2009 9:11 PM
  • Cory, since you seem to understand this quite well, I nominate you to post it on the connect site Once you do, could you post the link here?  And maybe give them a link to this discussion as well.

    Worst-case, they will say "by design" but hopefully they would also clarify exactly how to work around it.
    Wednesday, March 11, 2009 7:06 PM
  • I have submitted this issue to the Connect site. Here is the link.
    Wednesday, March 11, 2009 8:27 PM
  • The Connect site says they fixed it in .NET 4.0.
    Wednesday, March 25, 2009 8:04 PM
  • Microsoft has also released a patch for this ... for .NET 3.5:
    http://support.microsoft.com/?kbid=967328

    So ... you don't have to wait till .NET 4.0 ... if you don't want to. :)

    Wednesday, January 27, 2010 10:26 PM
  • I'm seeing this issue and have tried to obtain the hotfix, but there is no download link from the article. It says to contact MS support. The problem there is, there is only .Net 1.1 listed as a support product. Plus, there'a no way to open a .Net 1.1 support incident without already having a prepaid 5 incident contract! Has anybody have any luck downloading this?
    Monday, February 01, 2010 6:59 PM
  • I am having this problem in .NET3.5 and cannot obtain the hotfix. There is no product listing on the MS support website. Does anyone know how to get this? I would prefer not to pay for this, but emailing MS would cost ~$250.

    Help

    Wednesday, December 15, 2010 7:52 PM
  • There is no charge to get fixes from support.
    Thursday, December 01, 2011 2:44 AM