我的应用程序有一个特定的时间点,当许多大对象同时被释放时。那个时候,我想专门在大对象堆(LOH)上进行垃圾回收。
我知道你无法这样做,必须调用GC.Collect(2)
,因为仅当它执行第二代垃圾回收时,GC才会对LOH进行回收。然而,根据文档中的说明,调用GC.Collect(2)
仍会在第一代和第零代上运行GC。
是否可以强制GC 仅 收集第二代,而不包括第一代或第零代?
如果不可能,那么GC设计的原因是什么?
我的应用程序有一个特定的时间点,当许多大对象同时被释放时。那个时候,我想专门在大对象堆(LOH)上进行垃圾回收。
我知道你无法这样做,必须调用GC.Collect(2)
,因为仅当它执行第二代垃圾回收时,GC才会对LOH进行回收。然而,根据文档中的说明,调用GC.Collect(2)
仍会在第一代和第零代上运行GC。
是否可以强制GC 仅 收集第二代,而不包括第一代或第零代?
如果不可能,那么GC设计的原因是什么?
不可能。垃圾回收器是这样设计的:第二代垃圾回收会同时回收第一代和第零代。
编辑:在一位垃圾回收开发人员的博客中找到了一个相关的源:
第二代垃圾回收需要进行全面的回收(包括第零代、第一代、第二代和大对象!每次第二代垃圾回收都会对大对象进行回收,即使回收并非由大对象空间不足引起。请注意,并不存在只回收大对象的垃圾回收方式。)比年轻一代的回收时间要长得多。
编辑2:根据同一篇博客的第1部分和第2部分,显然第零代和第一代的回收速度比第二代快得多,因此仅进行第二代回收可能并不能带来很多性能优势。可能存在更根本的原因,但我不确定。也许这篇博客上有相关文章提供答案。
GC.Collect(2)
时,表示要求GC从Gen0、Gen1和Gen2进行收集。myVar = null
没有任何作用。请参见 http://www.bryancook.net/2008/05/net-garbage-collection-behavior-for.html 底部。 - DevinB回答“为什么”的问题:从物理上讲,Gen0、Gen1或Gen2并不存在。它们都使用虚拟地址空间上的同一内存块。它们之间的区别只是通过移动想象中的边界限制来虚拟地进行区分。
每个(小)对象都是从Gen0堆区域分配的。如果在收集后它幸存下来,它将被“向下”移动到托管堆块的那个区域,该区域最终刚刚从垃圾中释放出来。这是通过压缩堆来完成的。在完整的收集完成后,为Gen1设置的新“边界”是在那些幸存对象之后的空间。
因此,如果您只尝试清除Gen0和/或Gen1,您将在堆中打开空洞,必须通过压缩“完整”堆来关闭这些空洞 - 即使是Gen0中的对象也是如此。显然,这没有任何意义,因为其中大多数对象都是垃圾。没有必要将它们移动。在(否则紧凑的)堆上创建和留下大型空洞也没有意义。