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

J4N*_*J4N 11 c# profiler memory-leaks dynamic-language-runtime overflow

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

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

Wae*_*her 5

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

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

dynamic shell = Activator.CreateInstance(...);
foreach (object window in shell.Windows())
{ 
    var hwnd = window.Hwnd;
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

康桥

为了解决这个问题,我编写了一个名为“Combridge”的小助手类,用于释放 COM 对象并提供对底层 COM 对象的方法和属性的非常容易的访问。这非常简单明了,这里没什么特别的。它对COM 对象使用反射,这就是性能有所损失的原因(见下文)。

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

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

您可以查看文件ExplorerWindowActor,了解它在 RepoZ 中的使用方式。

它并不完全像 with 那样漂亮,dynamic并且在第一次尝试中性能也变得更糟。一个快速的工作台显示了以下内容:

表现

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

持续时间从 ~2500ms ( dynamic)上升到 ~6000ms ( Combridge)。每次调用的时间为 0.062 毫秒到 0.150 毫秒。

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

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

就是这样 - 我想和你分享这个故事,希望你也可以使用那个类(或它的改进版本)来摆脱动态地狱。

~更新~

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

在此处输入图片说明

因此,在打开 10 个资源管理器窗口、每个窗口 4 个 COM 调用以及整个循环每秒两次的情况下,RepoZ 创建了大约72.000 个 COM实例并总共进行了大约2.880.000 个 COM 调用,而内存消耗没有任何增加。

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


Tam*_*nut 1

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

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

鉴于您确实需要使用动态,我建议对您的代码进行检测并找出哪些部分最消耗内存。事实上,使用动态会增加内存消耗,因为它需要执行各种查找,但要出现内存不足异常,您需要对许多未知类型使用大量动态变量。

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

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