内存溢出:拥有越来越多的Microsoft.CSharp.RuntimeBinder.Semantics
我们正在寻找应用程序中的一些内存泄漏,在进行一些操作(在我们的应用程序中加载和关闭一个项目)时,我们知道内存总是增加一点点。
我们已经找到了很多它们,但是现在,10个以上增加最多的类是(根据我们的工具,ANTS Memory Profiler 8.2):
- Microsoft.CSharp.RuntimeBinder.Semantics.SYMTBL +键
- Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol
- Microsoft.CSharp.RuntimeBinder.Semantics.CONSTVAL
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCONSTANT
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCLASS
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRTYPEOF
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRLIST
- Microsoft.CSharp.RuntimeBinder.Semantics.MethWithInst
- Microsoft.CSharp.RuntimeBinder.Semantics.CMemberLookupResults
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRMEMGRP
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCALL
- Microsoft.CSharp.RuntimeBinder.Semantics.EXPRWRAP
- Microsoft.CSharp.RuntimeBinder.Semantics.AggregateDeclaration
- Microsoft.CSharp.RuntimeBinder.Semantics.Scope
不幸的是,我不是这个,所以我有点难以找到我应该释放的内容。
我检查了实例树,但是它一直都是微软的东西。
问题在于,当我们对项目进行“打开/关闭”时,我们会经历很多(大部分)代码。
编辑我们的应用程序的一部分使用dynamic
关键字作为某些资源,它可能是链接的。 这里的课程不是Disposable,我应该和他们做些什么吗?
编辑2
我很确定这与我的dynamic
相关,似乎C#在使用动态时创建了一个缓存。 但目前我不知道为什么它会增长(我一直加载相同的类,我将始终拥有完全相同的签名),也不知道如何清除它。
我通过在我的应用程序RepoZ中分析内存泄漏,今天遇到了完全相同的问题。 该工具应该在后台运行,检查Git存储库并定期更新Windows资源管理器窗口标题。 后一个任务必须对“Shell.Application”进行一些COM调用,以找到Explorer窗口并确定它们当前指向的路径。
通过使用像这样的dynamic
关键字……
dynamic shell = Activator.CreateInstance(...); foreach (object window in shell.Windows()) { var hwnd = window.Hwnd; ... }
……几个小时后,我结束了这样的内存转储:
Combridge
为了解决这个问题,我编写了一个名为“Combridge”的小助手类,它可以释放COM对象并提供对底层COM对象的方法和属性的轻松访问。 它非常简单直接,没什么特别的。 它利用了reflection到COM对象 ,这就是性能损失的原因(见下文)。
有了它,上面的代码示例如下所示:
using (var shell = new Combridge(Activator.CreateInstance(...))) { var windows = shell.InvokeMethod("Windows"); foreach (var window in windows) { var hwnd = window.GetPropertyValue("Hwnd"); ... } }
您可以在RepoZ中查看文件ExplorerWindowActor的使用方法。
它不像dynamic
那样漂亮,性能在第一次尝试中也变得更糟。 快速工作台显示如下:
性能
我测试了1000次迭代,在每次迭代中,处理了10个打开的资源管理器窗口。 对于每个窗口,在该COM对象上调用4个方法或属性。 所以我们谈的是40,000个COM调用。
持续时间从~2500ms( dynamic
)上升到~6000ms( Combridge
)。 每次通话都是0.062ms到0.150ms。
所以这需要大约2.4倍的时间才能完成。
我知道这很重要。 但是我的要求没问题,内存泄漏也没了。
就是这样 – 我想与你分享这个故事,希望你能用这个类(或它的改进版本)来摆脱动态的地狱。
〜〜更新
10个小时后,RepoZ仍然以非常恒定的内存占用率运行。
因此,打开10个资源管理器窗口,每个窗口进行4个COM调用,每秒循环两次,RepoZ创建了大约72.000个COM 实例 ,总共进行了大约2.880.000个COM调用 ,而不会增加内存消耗。
我想我们可以说这个问题真的伴随着dynamic
。
动态关键字应该很少使用,因为在大多数情况下,可以找到不需要它的变通方法。
根据您的应用,最好的建议是仔细考虑您是否可以设计您的解决方案,以避免动态。 以下是动态的一些有效用例: https : //msdn.microsoft.com/en-us/library/dd264736.aspx
鉴于您确实需要使用动态,我建议您检测代码并找出哪些部分占用的内存最多。 实际上,使用动态会增加内存消耗,因为它需要执行各种查找 ,但是如果出现内存不足的exception,则需要为很多未知类型使用大量动态变量。
在未知类型上调用方法有很多种不同的方法 ,测量和调整瓶颈是最佳选择。
PS:另外,发布一些代码片段有很大帮助。