GC.AddMemoryPressure

时间:2010-04-11 17:53:15

标签: c# garbage-collection

我在C#中编写一个使用第三方COM DLL的应用程序,这个dll在非托管内存中创建了大量资源(如位图,视频,数据结构)。在挖掘时,我遇到了垃圾收集器的以下调用:

GC.AddMemoryPressure(long long bytesAllocated)

MSDN中记录了这一点:

http://msdn.microsoft.com/en-us/library/system.gc.addmemorypressure.aspx

这听起来像我应该调用的东西,因为这个外部dll正在创建CLR不知道的大量资源。

我想我有两个问题......

  1. 我怎么知道当dll是第三方时要添加多少内存压力,而且我不可能确切地知道这个dll分配了多少内存。
  2. 这样做有多重要?

4 个答案:

答案 0 :(得分:5)

在任何混合的本机/托管进程中,存在本机/托管内存使用的混合。如果两者之间没有GC控制的关系,那么就不需要这个API。例如,如果托管代码中存在某些确定性状态更改导致本机内存被分配和释放,那么GC无法执行任何操作就会强制释放本机内存。

但是,通常由具有终结器的托管对象保留本机内存。因此GC 可以减少本机堆的大小,只需触发一个集合并让这些终结器运行即可。

因此,如果您有很多这样的事情,可能需要调用此API(就像文档所说的那样)。

至于你应该告诉它多少,你可能不会通过纯粹的分析来解决问题,特别是对于第三方库。您需要运行性能监视器,运行分配大量第三方对象的测试,并查看Native Bytes和CLR内存计数器以查看它们之间的关系。

当您使用COM对象时,实际上可以通过使用Marshal.ReleaseComObject确定性地强制实例在您不再需要它们时进行清理。请注意,您需要使用goofy循环来使其摆脱对象:

while (Marshal.ReleaseComObject(obj) != 0) 
{
}

答案 1 :(得分:3)

假设我有一个这样的对象:

public class SomeImageType : IDisposable
{
    public int Width { get; private set; }
    public int Height { get; private set; }
    public PixelFormat PixelFormat { get; private set; }
    IntPtr ImageData { get; private set; }
    // implementation of constructor and IDisposable not shown
}

这需要多少内存? 20个字节+对象开销?到了收集的时候,如果这个对象没有引用,它就没有任何区别。但它需要20个字节吗?没有 - 这是一个形象。因此,取20并将其乘以每个像素的宽度,高度和字节数,您就会占用(可能)兆字节。通过告诉GC增加内存压力,你说有一个冰山在那里咀嚼资源。当您处理对象时,您当然会释放该内存并告诉GC释放该压力。这两个调用让你向GC暗示,在Circle K上有一些奇怪的事情,也许它希望以不同的方式安排收集。

答案 2 :(得分:2)

您可以做的最重要的事情是在这些第三方DLL类上调用Dispose()方法,并且绝对狂热地做这件事。 (或者,当然,使用“使用”块,因为它们在从块退出时自动调用Dispose()。)

关于非托管资源的问题是,包装或使用它们的.NET类需要使用终结器,以及实现IDisposable()模式。如果调用Dispose(),它应该立即处理非托管资源,并且还应该抑制终结器。如果你没有调用Dispose(),那么你就会遇到真正的内存问题,并且向垃圾收集器添加“压力”将无法修复它。

垃圾收集器的基本逻辑是,在所有其他可能性已经用完之前,不会处理任何具有活动终结器的类。这意味着已经发生了gen0,gen1和gen2清理。未被抑制的终结器几乎与内存泄漏一样糟糕。

所以调用Dispose()。确保你永远不会错过它。

编辑:注意到第三方DLL是一个COM DLL。最佳做法是确保.NET包装器完全实现IDisposable()(而不仅仅是提供Dispose方法)。然后确保在完成COM对象时始终调用Dispose()。

答案 3 :(得分:1)

除非您注意到您认为由GC引起的内存问题,否则执行此操作并不重要。