微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – ReactiveUI绑定似乎阻止垃圾回收发生

我们正在使用ReactiveUI帮助构建一个相当大的基于 WPFWindows应用程序.一切顺利,直到我们发现我们的应用程序正在消耗大量的内存…基本上我们所有的观点,视图模型和模型都没有被垃圾回收.

基于内存分析器的信息,如Jet Brains dotMemory,ReactiveUI似乎是主要的祸根.特别是我们在我们的视图中配置的ReactiveUI绑定,即使我们使用最佳实践,并确保在视图被禁用时所有绑定都被处理.

以下是我们正在创建的其中一个视图的示例.任何关于我们可能会出错的想法将不胜感激.

public partial class RunbookInputsView : IViewFor<RunbookInputsviewmodel>
{
    public static readonly DependencyProperty viewmodelProperty = DependencyProperty.Register(
        "viewmodel",typeof(RunbookInputsviewmodel),typeof(RunbookInputsView));

    public RunbookInputsView()
    {
        InitializeComponent();

        this.WhenActivated(d =>
        {
            d(this.OneWayBind(viewmodel,vm => vm.AddInput,v => v.AddInput.Command));                
            d(this.OneWayBind(viewmodel,vm => vm.Inputs,v => v.Inputs.ItemsSource));
        });
    }

    object IViewFor.viewmodel
    {
        get { return viewmodel; }
        set { viewmodel = (RunbookInputsviewmodel)value; }
    }

    public RunbookInputsviewmodel viewmodel
    {
        get { return (RunbookInputsviewmodel) GetValue(viewmodelProperty); }
        set { SetValue(viewmodelProperty,value); }
    }
}

解决方法

从这个问题来看,很难说泄漏来自哪里.让泄漏发生一段时间,然后附加到windbg( Debugging Tools For Windows的一部分)的过程(注意:您可能需要构建x86或x64才能使其工作.)

一旦附加,通过输入命令设置.net调试:

.symfix
sxe clr
sxd av
.loadby sos clr

然后可以使用!dumpheap -stat来获取每种类型的内存使用情况.这产生以下格式的输出:(我截断了类名,列表中的可读性).

0:012> !dumpheap -stat
Statistics:
              MT    Count    TotalSize Class Name
000007fefa55d2e8        1           24 System.[...]TransportSinkProvider
000007fefa55ce08        1           24 System.Run[...]rtSinkProvider
000007fee7c32df0        1           24 System.LocalDataStoreHolder
000007fee7c2ff78        1           24 System.Colle[...]
000007fee7c2ece0        1           24 System.Resources.FastResourceComparer
000007fee7c2ead0        1           24 System.Resources.ManifestBasedResourcegroveler
000007fee7c2ea70        1           24 System.[...]eManagerMediator
000007fee4cc1b70        4         1216 System.Xml.XmlDocument

如果您有内存泄漏,那么您将看到泄漏的对象. (应该有很多它们).一旦你确定了什么泄漏,你可以做一个!dumpheap -type来获取一个实际对象的列表. (对于本例,我将使用System.Xml.XmlDocument.类型名称区分大小写,必须是完全限定的.)

0:012> !dumpheap -type System.Xml.XmlDocument
         Address               MT     Size
0000000002af9050 000007fee4cc1b70      304     
0000000002afa628 000007fee4cc1b70      304     
0000000002b0ea30 000007fee4cc1b70      304     
00000000037e2780 000007fee4cc1b70      304     

Statistics:
              MT    Count    TotalSize Class Name
000007fee4cc1b70        4         1216 System.Xml.XmlDocument

您的列表可能会更大,但是概率表示泄漏类型的任何随机实例将是您感兴趣的事情.如果我们在其中一个地址上执行操作,我们将获得类似于此的输出

0:012> !do 2af9050
Name:        System.Xml.XmlDocument
MethodTable: 000007fee4cc1b70
EEClass:     000007fee4ae7f00
Size:        304(0x130) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fee4cc2b40  40004fc        8   System.Xml.XmlNode  0 instance 0000000000000000 parentNode
000007fee4cc2258  400050a       10 ...XmlImplementation  0 instance 0000000002af9180 implementation
000007fee4cc22f0  400050b       18 ....Xml.DomNaMetable  0 instance 0000000002af92e0 domNaMetable
[Entries removed for clarity]
000007fee4cc26f0  400052f      108 ...m.Xml.XmlResolver  0 instance 0000000000000000 resolver
000007fee7c18c48  4000530      126       System.Boolean  1 instance                0 bSetResolver
000007fee7c113e8  4000531      110        System.Object  0 instance 0000000002af9788 objLock
000007fee4cc11b0  4000532      118 ....Xml.XmlAttribute  0 instance 0000000000000000 namespaceXml

您可以对表中列出的任何对象使用!做更多的信息.类似System.String和System.Boolean将会吐出它们的实际值.如果从创建的对象不清楚,下一步可能是使用!gcroot -nostacks来查找对象的引用.

0:012> !gcroot -nostacks 2af9050
HandleTable:
    00000000006117d8 (pinned handle)
    -> 0000000012a55748 System.Object[]
    -> 0000000002af9050 System.Xml.XmlDocument

Found 1 unique roots (run '!GCRoot -all' to see all roots).

有更多的命令,这已经太长了. !help命令提供了一个很好的列表. (要使用任何一个,您需要在命令前缀!!!help [command]提供有关特定命令的详细信息,例如!help dumpobj:

0:012> !help dumpobj
-------------------------------------------------------------------------------
!DumpObj [-nofields] <object address>

This command allows you to examine the fields of an object,as well as learn 
important properties of the object such as the EEClass,the MethodTable,and 
the size.

You might find an object pointer by running !DumpStackObjects and choosing
from the resultant list. Here is a simple object:

    0:000> !DumpObj a79d40
    Name: Customer
    MethodTable: 009038ec
    EEClass: 03ee1b84
    Size: 20(0x14) bytes
     (C:\pub\unittest.exe)
    Fields:
          MT    Field   Offset                 Type  VT     Attr    Value Name
    009038ec  4000008        4             Customer   0 instance 00a79ce4 name
    009038ec  4000009        8                 Bank   0 instance 00a79d2c bank

Note that fields of type Customer and Bank are themselves objects,and you can 
run !DumpObj on them too. You Could look at the field directly in memory using
the offset given. "dd a79d40+8 l1" would allow you to look at the bank field 
directly. Be careful about using this to set memory breakpoints,since objects
can move around in the garbage collected heap.

What else can you do with an object? You might run !GCRoot,to determine what 
roots are keeping it alive. Or you can find all objects of that type with 
"!DumpHeap -type Customer".

The column VT contains the value 1 if the field is a valuetype structure,and
0 if the field contains a pointer to another object. For valuetypes,you can 
take the MethodTable pointer in the MT column,and the Value and pass them to 
the command !DumpVC.

The abbreviation !do can be used for brevity.

The arguments in detail:
-nofields:     do not print fields of the object,useful for objects like 
                  String

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐