内存溢出:拥有越来越多的Microsoft.CSharp.RuntimeBinder.Semantics

时间:2015-10-12 11:42:16

标签: c# memory-leaks overflow profiler dynamic-language-runtime

我们正在寻找应用程序中的一些内存泄漏,在进行某些操作(在我们的应用程序中加载和关闭一个项目)时,我们知道内存总是增加一点。

我们已经找到了很多它们,但是现在,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#在使用动态时会创建一个缓存。但是目前我不知道它为什么会增长(我一直加载相同的类,我将始终拥有完全相同的签名),也不知道如何清除它。

2 个答案:

答案 0 :(得分:3)

我今天通过在我的应用RepoZ中分析内存泄漏来遇到完全相同的问题。该工具应该在后台运行,检查Git存储库并定期更新Windows资源管理器窗口标题。后一个任务必须对" Shell.Application"进行一些COM调用。找到资源管理器窗口并确定它们当前指向的路径。

使用dynamic这样的关键字......

dynamic shell = Activator.CreateInstance(...);
foreach (object window in shell.Windows())
{ 
    var hwnd = window.Hwnd;
    ...
}

......几个小时后我结束了这样的内存转储:

enter image description here

Combridge

为了解决这个问题,我写了一些helper class called "Combridge"来发布COM对象并提供对底层COM对象的方法和属性的轻松访问。它非常简单明了,没什么特别的。它利用Reflection to COM objects,这就是为什么会有一些性能损失(见下文)。

有了它,上面的代码示例如下所示:

using (var shell = new Combridge(Activator.CreateInstance(...)))
{
    var windows = shell.InvokeMethod<IEnumerable>("Windows");
    foreach (var window in windows)
    {
        var hwnd = window.GetPropertyValue<long>("Hwnd");
        ... 
    }
}

您可以在RepoZ中看到文件ExplorerWindowActor的使用方式。

它不像dynamic那样漂亮,并且在第一次尝试中表现也变差了。快速工作台显示如下:

性能

我测试了1000次迭代,在每次迭代中,处理了10个打开的资源管理器窗口。对于每个窗口,在该COM对象上调用4个方法或属性。所以我们谈论的是40,000个COM电话。

持续时间从~2500ms(dynamic)上升到~6000ms(Combridge)。每次通话的时间从0.062ms到0.150ms。

所以这需要大约2.4倍的时间才能完成。

这很重要,我知道。但是我的要求没问题,而且内存泄漏也消失了。

那是它 - 我想与你分享这个故事,希望你能用这个类(或它的改进版本)来摆脱动态的地狱。

〜更新〜

10小时后,RepoZ仍以非常恒定的内存占用率运行。

enter image description here

因此,打开10个资源管理器窗口,每个窗口4个COM调用,每秒两次完整循环,RepoZ创建 72.000个COM实例并进行 2.880.000个COM调用总体而言没有增加内存消耗。

我想我们可以说这个问题确实伴随着dynamic

答案 1 :(得分:1)

很少使用动态关键字,因为在大多数情况下,可以找到不需要它的变通方法。

根据您的应用程序,最好的建议是仔细考虑您是否可以设计您的解决方案,以避免动态。 以下是动态的一些有效用例:https://msdn.microsoft.com/en-us/library/dd264736.aspx

鉴于您确实需要使用动态,我建议您检测代码并找出哪些部分占用的内存最多。 确实使用动态会增加你的内存消耗,因为它需要执行各种lookups,但是为了有一个内存不足的异常,你需要使用很多动态变量来处理很多未知的事情。类型。

对于未知类型的调用方法有很多不同的ways,测量和调整瓶颈是可行的方法。

PS:另外,发布一些代码片段有很大帮助。