我在WPF导航中是否存在内存泄漏问题?

8
我正在查找一个WPF应用程序中的内存泄漏(使用ANTS Memory Profiler 5.1),但我发现一些页面和控件占用了本不应该占用的内存。
所以我去查看对象保留图并查看是什么在保留它们,但我发现每个页面都是这样: Object Retention Graph http://img683.imageshack.us/img683/3013/ants.jpg 问题是,我已经在每个页面上将KeepAlive设置为false,而我不认为用户控件上存在这样的属性。
有人能告诉我应该寻找什么吗?这是内存泄漏还是WPF应用程序的正常行为?
2个回答

9
是的,根据您提供的内容,您存在内存泄漏。当您找到引用链并且它不在您的代码中时,最简单的方法是使用反编译器Reflector。图片显示:JournalEntryKeepAlive._keepAliveRoot字段保存着该对象的引用。我们可以使用Reflector查看它是如何与我们的对象连接的。这次很容易,所有的线索都指向NavigationService.MakeJournalEntry()函数,然后是NavigationService.IsContentKeepAlive()。在这里:
internal bool IsContentKeepAlive()
{
    bool keepAlive = true;
    DependencyObject dependencyObject = this._bp as DependencyObject;
    if (dependencyObject != null)
    {
        keepAlive = JournalEntry.GetKeepAlive(dependencyObject);
        if (!keepAlive)
        {
            PageFunctionBase base2 = dependencyObject as PageFunctionBase;
            bool flag2 = !this.CanReloadFromUri;
            if ((base2 == null) && flag2)
            {
                keepAlive = true;
            }
        }
    }
    return keepAlive;
}

现在你知道规则了。如果对象满足以下条件,它将保留在内存中:

  • 它不是依赖对象;
  • 附加属性JournalEntry.KeepAlive为true;
  • 它不是PageFunction并且无法从Uri重新加载。

在调查后,阅读有关MSDN上的JournalEntry.KeepAlive属性可能会更有价值。

这种策略帮助我找到了许多与内存相关的问题。希望它也能帮助你 :).

PS:如果您仍然无法找到这个特定的泄漏问题,您可以粘贴最小的代码示例,以便我们重现它并给出更适当的答案。

祝福, Anvaka


谢谢您的详细解释,但我不确定我理解 DependencyProperty 的部分。如果我已经在每个页面上将 KeepAlive 设置为 false,为什么它仍然会保留在内存中? - Brandon
按照逻辑路径进行操作:即使您在每个页面上设置了KeepAlive=false,当flag2为true时,仍然可以从IsContentKeepAlive()获取true,这意味着:!CanReloadFromUri。 - Anvaka
3
你实际上改变了什么,以使得这个页面能够被垃圾回收? - Shaun Bowe
这是很好的研究。但是知道如何解决这个问题会更好。现在我唯一的猜测是删除后退入口,这有点违背了首先拥有NavigationService的初衷。 - ouflak

6
我曾遇到与Ants内存分析器相同的问题,也有相同的图表。 该应用程序使用了一个NavigationWindow来托管一些WPF页面,并且导航是通过此代码实现的:
NavigationService.Navigate( new Page1());

这个问题是由于期刊中存在多个页面而导致无法进行垃圾回收。

我的解决方法是将 NavigationWindow 替换为普通窗口,将页面替换为用户控件,并在窗口内交换用户控件,就像它们是页面一样。 在谷歌上有很多这样做的例子。 移除所有的NavigationService.Navigate调用之后,我最终可以使用Ants memory profiler回收所有已关闭的页面。


2
聪明。但是需要很多工作。而且,你不得不开发自己的导航服务吗?我曾经尝试过这样做,那真是一件麻烦事。 - ouflak
是的,正在构建自己的导航服务。然而,这是必要的,因为当您进行导航时会出现内存泄漏,例如 !this.CanReloadFromUri 的情况下,比如 Navigate(new Page()); - xmedeko

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