内存溢出:拥有越来越多的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:另外,发布一些代码片段有很大帮助。