将GC.AddMemoryPressure与非托管资源一起使用有什么意义?

dev*_*ium 14 .net c# vb.net garbage-collection

我已经通过c#在MSDN和CLR上阅读了这个问题.

想象一下,我们分配了一个2Mb的非托管HBITMAP和一个指向它的8字节托管位图.使用AddMemoryPressure告诉GC有什么意义,如果它永远无法对该对象做任何事情,因为它被分配为非托管资源,因此不易受垃圾收集的影响?

Ste*_*ons 12

提供它以便GC在收集期间知道对象的真实成本.如果对象实际上大于托管大小反映的对象,则它可能是快速(呃)收集的候选对象.

Brad Abrams 关于它的条目很清楚:

考虑一个具有非常小的托管实例大小但是拥有指向非常大的非托管内存块的指针的类.即使没有人引用托管实例,它也可以保持活动一段时间,因为GC只看到托管实例大小,它认为释放实例并不"值得".所以我们需要"教"GC关于这个实例的真实成本,这样它就能准确地知道何时踢出一个集合来释放更多的内存.

  • 请告诉我 GC.AddMemoryPressure() 在哪里以及如何与任何特定对象实例相关?如果没有关系,GC 将如何知道“对象的真实成本”? (3认同)
  • 当GC运行时,它并不关心对象在"决定"它是否是垃圾时与其关联的内存压力.GC压力的唯一影响是鼓励GC比其他情况更快地运行. (3认同)
  • 我觉得你理解很好.您的示例中缺少的一件事是,由于来自对象的内存压力,GC可能会启动集合(尽管它在使用时不会收集对象).除此之外,如果您的对象总是正确处理,我认为它不会为这些对象提供太多帮助.如果您无法控制对象的使用,那么使用这些API可能会更有用. (2认同)

Ran*_*pho 9

AddMemoryPressure的目的是告诉垃圾收集器,该对象分配了大量内存.如果没有管理,垃圾收集器就不知道了; 只有管​​理部分.由于托管部分相对较小,因此GC可能会多次通过垃圾回收,实质上浪费了可能需要释放的内存.

是的,您仍然需要手动分配和取消分配非托管内存.你无法摆脱这种局面.您只需使用AddMemoryPressure来确保GC知道它在那里.

编辑:

好吧,在第一种情况下,我可以做到,但它没有什么大的区别,因为GC无法对我的类型做任何事情,如果我理解正确:1)我声明我的变量,8个托管字节,2mb非托管字节.我然后使用它,调用dispose,因此释放了非托管内存.现在它只会占用8个字节.现在,在我看来,最后调用AddMemoryPressure和RemoveMemoryPressure并不会产生任何不同.我错了什么?很抱歉这对此很讨厌. - 豪尔赫布兰科

我想我看到了你的问题.

是的,如果你可以保证你总是打电话Dispose,那么是的,你不需要打扰AddMemoryPressure和RemoveMemoryPressure.没有等价,因为引用仍然存在,并且永远不会收集类型.

也就是说,为了完整起见,您仍然希望使用AddMemoryPressure和RemoveMemoryPressure.例如,如果您班级的用户忘记拨打Dispose,该怎么办?在这种情况下,假设您正确实现了Disposal模式,您将最终在完成时回收您的非托管字节,即收集托管对象时.在这种情况下,您希望内存压力仍然有效,以便更有可能回收对象.


Dre*_*kes 7

这些方法使运行时能够了解进程正在分配多少非托管内存。如果不调用这些函数,它可能无法看到进程内使用的非托管内存的真实数量。

\n\n

然而,我不同意这里关于所引用的内存和特定 GC 对象之间的关联的其他答案。

\n\n

考虑:

\n\n
var buffer = IntPtr.Zero;\ntry\n{\n    buffer = Marshal.AllocHGlobal(size);\n    GC.AddMemoryPressure(size);\n\n    // ... use buffer ...\n}\nfinally\n{\n    Marshal.FreeHGlobal(buffer);\n    GC.RemoveMemoryPressure(size);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

GC 无法将 分配size给特定对象。该代码甚至可以存在于静态方法中。

\n\n

因此我断言我们需要 \xe2\x80\x9cteach\xe2\x80\x9d 声明 GC 有关此实例的真实成本,以便它准确地知道何时启动收集以释放进程中的更多内存是不正确且具有误导性的。

\n\n

相反,此方法可能会导致 GC 比其他方式更早进行收集,以避免内存不足。

\n