2

我有一个使用 MVVM Prism 编写的 WPF 应用程序。它有很多标签。这些选项卡每个消耗大约 2..3 MB 的内存。客户抱怨说,在打开和关闭几十个选项卡后,应用程序消耗的内存比一开始时要多得多。此外,打开新标签需要更多内存,因此应用程序不会使用旧标签而是创建新标签。

所以很明显旧标签不是垃圾收集的。显然是因为有一些链接指向它们。

我如何以任何方式收集它们?我应该只实现 IDisposable,并删除所有可能的引用吗?并确保在析构函数中也调用了 Dispose 方法?我不确定是否可以删除所有不需要的引用。

也许我可以使用一个很好的工具来帮助我解决这个问题?

4

1 回答 1

4

这显然是一个加载的问题。内存消耗可能不仅是由内存泄漏引起的!每个应用程序都是不同的,并且没有灵丹妙药,所以这里有一些可能会有所帮助的想法。

  1. 获取 ANTS 分析器,它有 2 周的试用期 + 很棒的教程。它会为您指出很多东西,例如僵尸对象等。

  2. WPF 是一头猪,因此您打开的选项卡越多(不会消失的控件),它将占用更多的内存。看看你的 XAML,你能把它修整一下。例如,使用 TextBlock 代替标签。删除额外的嵌套控件,例如 StackPanel 中的 StackPanel 或 Grid 中的 Grid,在 stackpanel 中 - 将所有这些控件放在一个网格中,并使用行/列.. 如果您有一个项目模板,其中每个项目都执行某些操作它很复杂,尝试改变它。例如,如果在焦点上它会在项目周围画一个边框并做一些花哨的事情,问一个问题,我可以从每个项目中删除它,并创建一个控件来计算它的位置并适当地放置自己。

  3. 每个选项卡是否有相同类型的视图(用户控件或控件),只是不同的实例?如果是这样,您可以回收它们吗?一年多以前,我在一个项目中,我们有一些菜单为一些标准创建一个选项卡,然后是另一个,另一个。标准不同,但视图类型相同。它只是被注入了不同的信息——Prism 正在为每个选项卡创建一个新的视图控件,这显然很昂贵。我们最终做的是创建不同的 ViewModel,但保留该昂贵视图的相同实例(通过在需要时删除/添加回该区域来回收)。为此,每个 ViewModel 都会在导航上(示例)

    //detaching from Prism region allows for recycling
    public override void OnNavigatedFrom (NavigationContext navigationContext)
    {
     var view = _container.Resolve (typeof (Object), "NameOfTheView");
     if (view != null)
        navigationContext.NavigationService.Region.Remove (view);
    }
    //similarly you can readd it where your think it is nedded..
    
    public override void OnNavigatedTo (NavigationContext navigationContext)
    {
        base.OnNavigatedTo (navigationContext);
        RestoreDataState (_state);
    

    }

    此链接可能很有用:http: //blogs.msdn.com/b/dphill/archive/2011/01/23/closable-tabbed-views-in-prism.aspx

  4. 垃圾收集注意事项:.NET 垃圾收集器的全部目的是代表我们管理内存。但是,在一些非常罕见的情况下,使用 GC.Collect() 以编程方式强制垃圾收集可能是有益的。

    具体来说:

    a.当您的应用程序即将进入您不希望被可能的垃圾收集中断的代码块时。

    b.当您的应用程序刚刚完成分配大量对象并且您希望尽快删除尽可能多的获取内存时。(我的项目就是这种情况)

    C。也:http: //blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx

我肯定会在每个 ViewModel 上实现 Dispose 方法,我会将所有大的东西都设置为 null,不要忘记取消订阅事件以及计时器之类的东西。你可以调用 GC.Collect (); 但请阅读上面的注释。不要忘记清理复杂对象,不要只是将它们设置为空。例如,在我的 Dispose 中,我们有类似的内容:

ClearDisplayGrid ();

反过来又这样做了:

private void ClearDisplayGrid ()
    {
        foreach (var r in DisplayGrid.MyItems.SelectMany (it => it.SubItems))
        {
            r.IsSelectedChanged -= ReadingIsSelectedChanged;
            r.InEditChanged -= ReadingInEditChanged;
            r.PropertyChanged -= ReadingPropertyChanged;
        }
    }
于 2012-04-19T16:51:01.333 回答