[英]Poor Automatic C# Garbage Collector Performance with many C++ Interop Calls
什么可能导致C#垃圾收集失败如此悲惨地在我的C#应用程序执行大量的C ++调用时放置好GC.Collect解决问题? 我的C#app使用System.Runtime.InteropServices DllImport和CallingConvention.Cdecl进行数百万次C ++调用,并有一些C#析构函数来释放一些C ++非托管内存。 我正在使用.NET Framework 4。
什么可能导致我的应用程序中的下面的代码强制进行分页,从而减慢执行到爬行(在我的32 GB系统上消耗29GB的RAM并且在我杀死进程之前花费超过4分钟)而只是将ManualGC
更改为true
大写内存使用量约为600MB,执行在29秒内完成?
为什么将ManualGC
保留ManualGC
false
并将Write
更改为大约12 GB的true
内存使用量,并允许执行在大约59秒内完成而不进行分页?
我的应用程序中的一些代码片段(显然有些名称已更改):
private static int callCount = 0;
private const bool ManualGC = false;
private const bool Write = false;
internal static void CommonlyCalled()
{
++callCount;
if ( callCount % 100000 == 0)
{
if (ManualGC)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
if (Write) Console.WriteLine(HandleErrorsCallCount);
}
DoLogic();
}
使用Windows任务管理器中的“内存(专用工作集)”列测量内存。 行为始终是可重复的。
并有一些C#析构函数来释放一些C ++非托管内存
如果您的包装器很小并且您的C ++代码需要大量内存,那还不够。 你只是没有给GC施加足够的压力让它尽快给你的终结者打电话。 实现IDisposable来解决这个问题是样板。 但不是一个完整的解决方案,你应该告诉GC,以便它可以做些什么。 一些代码可以使用:
using System;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
while (!Console.KeyAvailable) {
new Wrapper();
}
}
}
class Wrapper {
private const int alloc = 10 * 1024; // C++ object memory usage
private readonly bool useamp = false; // Change this after testing
private IntPtr mem;
public Wrapper() {
if (useamp) GC.AddMemoryPressure(alloc);
mem = Marshal.AllocHGlobal(alloc);
}
~Wrapper() {
Marshal.FreeHGlobal(mem);
if (useamp) GC.RemoveMemoryPressure(alloc);
}
}
运行它时,请观察该程序的内存使用情况。 在我的机器上,私有字节构建大约半个演出。 现在改变useamp
并再次运行,你会看到它是有效得多 ,只需要4 MB。 无需调用Dispose :)在Win 8.1,.NET 4.5.1上测试,在较旧的.NET版本上可能会得到非常不同的结果。
你为alloc选择的价值并不重要,它只需要在球场上。 显然你确实需要超过10KB。
听起来您依赖于用户定义的终结器来在C#应用程序中正确行为。 您需要更新C#代码以解决此方案的以下特定问题:
SafeHandle
的类,以便只包含资源的句柄。 SafeHandle
实例上显式调用Dispose()
,当您完成使用它们时,它会包装您的非托管资源。 永远不要依赖C#中的终结器来执行清理操作。 1这个陈述足够真实,以下几乎是一条生活规则:
如果你用C#编写用户定义的终结器,你可能做错了什么。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.