WPF内存泄漏问题

4
我有一个WPF表单,但我不是很擅长WPF技术,因为这个表单泄漏非常严重,高达400 MB,关闭表单也没有帮助。
问题在于我的应用程序一次性加载了所有图片。我希望只加载当前可见的图片。大约有300张图片,它们有点大,所以我的WPF表单在加载它们时会受到影响。
我有一个DataTemplate,它有自己的类型和一个Thumbnail属性。模板中的代码如下:
            <Image Source="{Binding Path=Thumbnail}" Stretch="Fill"/>

然后我有一个带有上述模板作为源的控件的网格。该控件的代码如下。请提供关于如何优化代码并仅获取可见的控件以及同一时间只加载那么多控件的提示?

    <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>
3个回答

4
寻找.NET应用程序(无论是WPF还是其他类型)中的内存泄漏问题,第一步就是查找订阅事件的对象。如果对象X正在侦听由对象Y引发的事件,则Y持有对X的引用。无论您实现了什么虚拟化(或处理)方法,如果X不取消订阅Y的事件,则X将保留在对象图中,只要Y存在,它就不会被终结和垃圾回收。(即使它实现了IDisposable接口,您显式调用Dispose方法也无济于事。)
当您说“关闭窗体没有帮助”时,这让我更加怀疑:我预计有人在Window对象上实现了一个对象属性,并且该对象已订阅了某种事件。因此,您关闭窗口,但它仍然存在于对象图中,因为其中一个属性正在被引用。
(为了让您了解这可能有多阴险:WinForms ToolStrip控件在显示时会订阅Windows主题更改事件。这非常棒,因为在更改计算机的主题时自动反映在运行中应用程序的UI中。但是,如果您在先将Visible设置为false之前取消引用ToolStrip,则它将继续接收主题更改事件,直到应用程序终止。)
内存分析器可以帮助解决这个问题-这就是我发现我的应用程序尽管我认为所有对象都已被销毁,但仍具有成千上万个ToolStrip对象的原因。

+1:我们公司的WPF应用程序每次生成新页面时都会泄漏数兆字节的内存,我们都认为这是WPF的一个错误。使用分析器后发现每个页面都被连接到静态事件处理程序,但从未解除连接,导致了灾难性(且很容易修复)的泄漏问题。 - Juliet

2
“ElementFlow”控件是否与此处所描述的相同?看起来该控件已经使用虚拟化, 所以我不希望它访问一个不可见项的缩略图属性。
你如何建模暴露“Thumbnail”属性的数据结构?能否设置它,使得该属性在第一次访问时需求加载缩略图?也许使用支持缓存(在一段时间内保持缩略图已加载)实现这一点可以解决问题。 编辑 我可能做出了错误的假设。通过阅读我链接的第二篇文章的评论,我现在认为公开版本的ElementFlow控件实际上没有实现虚拟化。也许你可以记录对“Thumbnail”属性的访问,并确定是否访问了非可见元素的属性。

谢谢。我已经检查并可以验证,缩略图属性将被检索与列表中的项目数量相同的次数。是的,那就是我正在使用的控件 :). 听起来他也发现了同样的事情。实现虚拟化有多难呢..? :) - Oskar Kjellin

0

仅凭您的代码片段很难缩小问题范围。如果您还没有使用Visual Profiler,建议您尝试一下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接