触发gen2垃圾收集的原因是什么?

时间:2011-05-26 23:45:04

标签: .net memory-management c#-4.0 garbage-collection

我有一个奇怪的情况,我想弄清楚。

创世纪:

我在具有 16 内核和 128GB 内存的物理计算机上运行我的程序。我试图确定为什么它没有使用所有可用的内核,通常它平均使用20-25%的CPU(因此16个中的4-5个内核)。当我查看性能计数器时,它们显示垃圾收集时间为60-70%。

作为参考,我使用.NET Framework 4和TPL(Parallel.ForEach)来编写程序的性能密集型部分。我将线程数限制为核心数。

问题:

我正在创建大量对象,对于垃圾收集器来说有效处理太多,因此它在垃圾收集器中花费了大量时间。

到目前为止的简单解决方案:

我正在引入对象池以减少垃圾收集器的压力。我将继续汇集对象以提高性能,已经将一些对象集中在一起,将垃圾收集从60-70%减少到45%的时间,我的程序运行速度提高了40%。

唠叨问题(我希望你能回答我的问题):

我的程序在运行时使用最多14GB的可用内存,相比之下128GB的内存则非常小。这台机器上没有其他东西在运行(它纯粹是我的测试台),并且有足够的RAM可用。

  • 如果有足够的RAM可用,为什么要发生任何gen2(或完整)集合呢?相当多的这些gen2集合(成千上万)正在发生。即如何确定启动gen2集合的阈值?
  • 为什么垃圾收集器不会直接延迟任何完整的收集,直到物理RAM上的压力达到更高的阈值?
  • 有什么方法可以配置垃圾收集器等待更高的阈值? (如果没有必要的话,根本不打算收费)

修改

我已经在使用服务器垃圾收集器了...我需要知道的是什么是触发gen2集合,而不是服务器垃圾收集器更好(我已经知道了)。

2 个答案:

答案 0 :(得分:19)

我记得,客户端GC是默认值。我的经验是它在收集之前不会让堆变得非常大。对于我的重型处理应用程序,我使用“服务器”GC。

在应用程序配置文件中启用服务器GC:

<?xml version ="1.0"?>
<configuration>
  <runtime>
    <gcServer enabled="true"/>
  </runtime>
</configuration>

这对我来说在性能方面存在巨大的差异。例如,我的一个程序花费了超过80%的时间用于垃圾收集。启用服务器GC将其降低到略高于10%。内存使用量上升是因为GC让它发挥作用,但这对我的大多数应用程序来说都很好。

将导致Gen 2集合的另一件事是大对象堆。见CLR Inside Out: Large Object Heap Uncovered。简而言之,如果超过LOH阈值,它将触发Gen 2集合。如果你要分配很多短寿命大对象(大约85千字节),这将是一个问题。

答案 1 :(得分:5)

从模糊的记忆和阅读:http://msdn.microsoft.com/en-us/library/ee787088.aspx,我认为Gen 2 GC的一个触发器可以是第2代段填充。文章指出Server GC使用更大的段,如前所述,这可能对您的性能很重要。

让机器等到它几乎没有任何无内存将意味着你在某个阶段得到一个地狱的GC。这可能不太理想。如果您在GC中的时间如此之高,那就表明您正在分配太多的物体,这些物体的存活时间足以超过第0代和第5代。 1,并以重复的方式进行。如果应用程序的内存使用量没有随着时间的推移而上升,则表明这些对象实际上是短暂的,但是活得足够长,可以在0和1集合中存活。这是一个糟糕的情况 - 您正在分配一个短期对象,但需要支付完整的第2代收集成本来清理它。

如果是这种情况,您可以采取一些不同的指示:

  1. 尽量让短寿命物品更快收集(因此它们不能生成第2代,因此GC成本较低)
  2. 尝试分配较少的短期对象(因此,在分配强制GC并将对象移动到较旧代之前,您可以更少地使用GC并且您有更多时间来完成使用短期对象)
  3. 使用堆栈分配值类型而不是短期对象的引用类型(如果它适合您的目的)
  4. 如果您知道需要大量这些对象,请将它们预先集中在一起。听起来你正在这样做,但仍然需要进行大量的分配以保持GC达到45%。如果您的游泳池不够大,请提前分配更多 - 正如您所说,您有足够的备用内存。
  5. 所有这些的组合可能是一个很好的解决方案。你需要很好地理解你分配的对象,他们的生活时间,以及他们实际需要多长时间来实现你的目的。

    GC对生命周期较短的临时对象(如GC可快速收集)或长期/永久性对象的生命周期很满意。在这两个类别的中间分配大量对象是你痛苦的地方。因此,分配较少的或更改其生命周期以匹配其使用场景。