.net GC.仅在第2代上收集&;大对象堆

.net GC.仅在第2代上收集&;大对象堆,.net,garbage-collection,large-object-heap,.net,Garbage Collection,Large Object Heap,在我的应用程序中,有一个特定的时间,许多大型对象同时被释放。当时我想对大型对象堆(LOH)进行垃圾收集 我知道您不能这样做,您必须调用GC.Collect(2),因为GC只有在执行第2代收集时才会在LOH上调用。但是,我在文档中读到,调用GC.Collect(2)仍然会在第1代和第0代上运行GC 是否可以强制GC仅收集gen 2,而不包括gen 1或gen 0 如果不可能,是否有理由将GC设计成这样?这是不可能的。GC的设计使第2代收集始终也收集第0代和第1代 编辑:在以下网站上找到此文件的来源

在我的应用程序中,有一个特定的时间,许多大型对象同时被释放。当时我想对大型对象堆(LOH)进行垃圾收集

我知道您不能这样做,您必须调用
GC.Collect(2)
,因为GC只有在执行第2代收集时才会在LOH上调用。但是,我在文档中读到,调用
GC.Collect(2)
仍然会在第1代和第0代上运行GC

是否可以强制GC仅收集gen 2,而不包括gen 1或gen 0


如果不可能,是否有理由将GC设计成这样?

这是不可能的。GC的设计使第2代收集始终也收集第0代和第1代

编辑:在以下网站上找到此文件的来源:

Gen2 GC需要完整的集合 (Gen0、Gen1、Gen2和LOH!大 对象在每个Gen2 GC中进行GC 即使GC不是由 LOH中缺少空间。请注意 不是只收集大型数据的GC吗 对象),这比 年轻一代收藏

编辑2:在同一个博客中,高效地使用GC,显然Gen0和Gen1集合比Gen2集合要快,因此我认为仅使用Gen2不会带来太多性能好处是合理的。可能还有更根本的原因,但我不确定。答案可能在该博客上的某篇文章中。

由于所有新分配(大型对象除外)始终都在Gen0中,因此GC设计为始终从指定的一代及以下进行收集。当您调用
GC.Collect(2)
时,您告诉GC从Gen0、Gen1和Gen2进行收集

如果您确定要处理大量大型对象(在分配时大到足以放置在LOH上的对象),那么最好的选择是确保在处理完这些对象后将其设置为null(VB中没有)。LOH分配尝试智能化和重用块。例如,如果您在LOH上分配了一个1MB对象,然后处理了它并将其设置为null,那么您将留下一个1MB的“洞”。下次在LOH上分配1MB或更小大小的任何内容时,它将填充该孔(并继续填充,直到下一次分配太大而无法容纳剩余空间,此时它将分配一个新块)

请记住,.NET中的代不是物理的,而是逻辑分离,有助于提高GC性能。因为所有新的分配都在Gen0中,所以它始终是要收集的第一代。每运行一个收集周期,较低一代中任何在收集过程中幸存下来的东西都会“升级”到下一个最高一代(直到第2代)

在大多数情况下,GC不需要超出收集Gen0的范围。GC的当前实现能够同时收集Gen0和Gen1,但在收集Gen0或Gen1时无法收集Gen2。(.NET 4.0极大地放松了这一限制,在大多数情况下,GC能够在收集Gen0或Gen1的同时收集Gen2。)

要回答“为什么”这个问题:实际上,没有Gen0和Gen1或Gen2这样的东西。它们都在虚拟地址空间上使用相同的内存块。它们之间的区别实际上只是通过围绕一个假想的边界边界移动来实现的

每个(小)对象都是从Gen0堆区域分配的。如果在一次收集之后,它仍然存在,它将“向下”移动到托管堆块的那个区域,该区域最终刚刚从垃圾中释放出来。这是通过压缩堆来完成的。在完整收集完成后,Gen1的新“边框”将设置为这些幸存对象后面的空间


因此,如果您想出去尝试清除Gen0和/或Gen1,您将在堆中打开一些洞,这些洞必须通过压缩“完整”堆来关闭,甚至是Gen0中的对象。显然,这没有任何意义,因为这些对象中的大多数无论如何都是垃圾。移动它们没有任何意义。在堆上创建和留下大洞(否则会压缩)是没有意义的。

每当系统对特定的一代执行垃圾收集时,它必须检查可能包含对该代任何对象的引用的每个对象。在许多情况下,旧对象将只保留对其他旧对象的引用;如果系统正在进行Gen0收集,则可以忽略任何仅包含对Gen1和/或Gen2的引用的对象。同样,如果它正在进行Gen1收集,它可以忽略任何只包含对Gen2的引用的对象。由于对象的检查和标记代表了垃圾收集所需的大部分时间,因此能够完全跳过较旧的对象代表了相当大的时间节省

顺便说一句,如果您想知道系统如何“知道”一个对象是否可能包含对较新对象的引用,那么系统有一个特殊的代码,用于在写入对象时在每个对象的描述符中设置一些位。第一位在每次垃圾收集时重置,如果在下一次垃圾收集时仍然重置,则系统将知道它不能包含对Gen0对象的任何引用(因为上次写入对象时存在且未被上一次收集清除的任何对象将是Gen1或Gen2)。第二位在每个Gen1垃圾收集时重置,如果在下一个Gen1垃圾收集时仍然重置,系统将知道它不能包含对Gen0或Gen1对象的任何引用(它持有引用的任何对象现在都是Gen2)。请注意,系统不知道或不关心写入对象的信息是否包含Gen0或Gen1引用。写入未标记对象时所需的陷阱为