views:

228

answers:

3
+4  Q: 

WPF Memory Leak

I have an WPF form that I myself did not create, so I am not very good at WPF. It is leaking badly though, up to 400 MB and closing the form does not help.

The problem lies in my application loading all the pictures at once. I would like to only load the ones visible at the moment. It is about 300 pictures and they are a bit large so my WPF-form suffers from loading them all.

I have a DataTemplate with my own type that has a property Thumbnail. The code in the template is like this:

            <Image Source="{Binding Path=Thumbnail}" Stretch="Fill"/>

And then I have a grid with a control that has the above template as source. The code for this control is the below. Please provide me with hints on how to optimize the code and perhaps get the only ones that are visible and only have that many controls loaded at the same time?

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Controls:ElementFlow">
                <Grid Background="{TemplateBinding Background}">
                    <Canvas x:Name="PART_HiddenPanel"
                            IsItemsHost="True"
                            Visibility="Hidden" />
                    <Viewport3D x:Name="PART_Viewport">
                        <!-- Camera -->
                        <Viewport3D.Camera>
                            <PerspectiveCamera FieldOfView="60"
                                               Position="0,1,4"
                                               LookDirection="0,-1,-4"
                                               UpDirection="0,1,0" />
                        </Viewport3D.Camera>

                        <ContainerUIElement3D x:Name="PART_ModelContainer" />

                        <ModelVisual3D>
                            <ModelVisual3D.Content>
                                <AmbientLight Color="White" />
                            </ModelVisual3D.Content>
                        </ModelVisual3D>
                <Viewport2DVisual3D
        RenderOptions.CachingHint="Cache"
        RenderOptions.CacheInvalidationThresholdMaximum="2"
        RenderOptions.CacheInvalidationThresholdMinimum="0.5"/>
                    </Viewport3D>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
A: 

It is hard to narrow down the problem with just your code snippet. You might want to Visual Profiler if you haven't already.

iaimtomisbehave
+2  A: 

Is the 'ElementFlow' control the same one described here? It appears that control is already using virtualization, so I wouldn't expect it to access the Thumbnail property of a non-visible item.

How are you modelling the data structure that exposes the 'Thumbnail' property? Can you set it up such that the property demand-loads the thumbnail on first access? Perhaps implementing this with a backing cache (that keeps the thumbnails loaded for a period of time) would address the issue.

EDIT

I may have assumed something I should not have. In reading the comments to the second post I linked, I now think it may be that the publicly available version of the ElementFlow control does not, in fact, implement virtualization. Perhaps you could log access to the 'Thumbnail' property and determine if the property is accessed for non-visible elements.

Daniel Pratt
@Daniel Thanks. I checked and can verify that the Thumbnail property is retrieved as many times as there is items in the list. And yes that is the control I am using :). Sound like he discovered the same thing. How hard it is to implement virtualization..? :)
Oskar Kjellin
+3  A: 

The first place to look at when you're trying to find memory leaks in a .NET application, WPF or not, is objects that subscribe to events.

If object X is listening to an event raised by object Y, then Y holds a reference to X. Whatever virtualization (or disposal) method you implement, if X doesn't unsubscribe from Y's event, X will stay in the object graph as long as Y does, and will never get finalized and garbage-collected. (Even if it implements IDisposable and you explicitly call Dispose on it.)

When you say "closing the form does not help," that makes me doubly suspicious: I'd expect that someone's implemented an object property on the Window object, and that object has subscribed to an event of some kind. So you close the window, but it still exists in the object graph because one of its properties is being referenced.

(To give you an idea of how insidious this can be: WinForms ToolStrip controls subscribe to Windows theme-change events when they become visible. This is great in that changing your computer's theme is automagically reflected in the UI of your running application. It is not so great in that if you dereference a ToolStrip without first setting Visible to false, it will continue receiving theme-change events until the application terminates.)

A memory profiler can help with this - that's how I discovered that my application had thousands of ToolStrip objects in memory even though I thought all of them had been destroyed.

Robert Rossney
+1: my company's WPF app was leaking megabytes of memory everytime it generated a new page, and we all assumed it was a bug with WPF. Whipped out the profiler and found that every page was being wired up to static event handlers, but never unwired, resulting in a catastrophic (and easily fixable) leak.
Juliet