C# GC.AddMemoryPressure

C# GC.AddMemoryPressure,c#,garbage-collection,C#,Garbage Collection,我正在用C#编写一个应用程序,它使用第三方COM DLL,该DLL在非托管内存中创建大量资源(如位图、视频、数据结构)。在四处挖掘时,我遇到了以下关于垃圾收集器的呼吁: GC.AddMemoryPressure(long long bytesAllocated) MSDN中记录了以下内容: 这听起来像是我应该调用的东西,因为这个外部dll正在创建许多CLR不知道的资源 我想我有两个问题 当dll是第三方时,我如何知道要添加多少内存压力,并且我不可能确切地知道这个dll分配了多少内存 这样做有

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

GC.AddMemoryPressure(long long bytesAllocated)
MSDN中记录了以下内容:

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

我想我有两个问题

  • 当dll是第三方时,我如何知道要添加多少内存压力,并且我不可能确切地知道这个dll分配了多少内存
  • 这样做有多重要

  • 这样做对您来说并不重要,除非您注意到您认为是由GC引起的内存问题。

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

    非托管资源的问题在于,包装或使用它们的.NET类需要有一个终结器,并实现IDisposable()模式。如果调用Dispose(),它应该立即处理非托管资源,并且还应该抑制终结器。如果不调用Dispose(),则会出现真正的内存问题,向垃圾收集器添加“压力”无法解决此问题

    垃圾收集器的基本逻辑是,任何具有活动终结器的类都不会被处理,直到所有其他可能性都已用尽。这意味着已经进行了第0代、第1代和第2代清理。没有被抑制的终结器几乎和内存泄漏一样糟糕

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


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

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

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

    因此,如果您有很多这样的事情,那么很有必要调用这个API(就像文档中所说的那样)

    至于你应该说多少,这可能不是你可以通过纯粹的分析编造出来的答案,特别是在第三方图书馆。您需要运行性能监视器,运行分配大量第三方对象的测试,并查看本机字节和CLR内存计数器以了解它们之间的关系

    当您使用COM对象时,事实上,您可以确定地强制实例在您知道不再需要它们时进行清理,方法是使用。请注意,您需要使用goofy循环使其摆脱对象:

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

    假设我有一个这样的物体:

    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提示,圆圈K中正在进行一些奇怪的事情,也许它希望以不同的方式安排收集。

    您不太可能需要使用此API,但只是出于好奇,“创建大量资源”(这是一个非常模糊的说法)背后有哪些数字?特别是,分配的内存量是多少?可能是兆字节(甚至几十兆字节)的数据,确切的大小取决于视频量、编码,有很多因素可能会影响这一点。我希望找到一种方法来知道在初始化这个特定的com对象之前和之后有多少非托管内存被使用,这样我就可以使用某种启发式方法来找出需要添加的合理内存压力。具体细节我不知道,因为代码是第三方的。我非常热衷于使用Dispose,所以听起来我应该很好。再次感谢!当对包装类调用
    Dispose
    时,它应该做什么?(我已经用这个问题的答案更新了我的答案!)好吧,很酷,手动告诉运行时可调用包装器释放其所有引用(同时要非常小心,不要有任何可能引发System.NullReferenceException的错误指针)。在内存使用方面,我查看了性能计数器,但根据视频大小,内存可能会发生变化,所以这没有多大帮助。我希望能找到一种方法动态地解决这个问题。。。这个dll可以使用一段时间,因此让GC知道background.NB中有一个内存大象似乎是有意义的。如果您始终以确定的方式强制COM对象关闭,那么您就处于我在第一段中描述的情况:GC只需要负责它可以直接检测的内存使用,等等