【问题标题】:What triggers a gen2 garbage collection?什么触发了 gen2 垃圾收集?
【发布时间】:2011-05-26 23:45:04
【问题描述】:

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

创世纪:

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

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

问题:

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

迄今为止的简单解决方案:

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

烦人的问题(希望你能帮我解答):

我的程序在运行时最多使用 14GB 的可用 RAM,与 128GB 的​​ RAM 相比,这非常小。这台机器上没有运行其他任何东西(对我来说它纯粹是一个测试平台)并且有大量可用的 RAM。

  • 如果有大量可用 RAM,为什么会发生任何 gen2(或完整)集合?相当大量的这些 gen2 集合(数以千计)正在发生。即如何确定开始 gen2 收集的阈值?
  • 为什么垃圾收集器不简单地延迟任何完整的收集,直到物理 RAM 上的压力达到更高的阈值?
  • 有什么方法可以配置垃圾收集器等待更高的阈值? (即如果没有必要,根本不用费心收集)

编辑:

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

【问题讨论】:

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


    【解决方案1】:

    我记得,客户端 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 KB),这将是一个问题。

    【讨论】:

    • 我已经在使用服务器垃圾收集器(由于每个核心规则一个堆,它在许多核心上性能更高)。虽然我很欣赏你的有用建议,但这并没有真正回答我的问题:(
    • 这很好。不过我不认为我正在这样做,我的 LOH 堆上通常只有一个数组,但我会更深入地检查一下。
    • 哇!显然我是个非常愚蠢的人。我的程序中有 而不是 。显然,它是区分大小写的!! >-( 我进行了更改,发现性能提高了近 10 倍。% GC 时间平均从 45% 变为 12.5%。我仍然会返回并继续池化对象以从中挤出更多性能,但没有你的评论 我从没想过要回去检查垃圾收集器实际上是否设置为服务器。再次感谢!
    • @Jeffrey:我希望我可以说我从未这样做过。我不认为我已经使用 gcServer 完成了它,但是我之前在配置文件中遇到过区分大小写的问题。很高兴你把它修好了。
    【解决方案2】:

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

    让机器等到它几乎没有可用内存意味着你在某个阶段会遇到一次地狱般的 GC。这可能并不理想。如果您在 GC 中的时间如此之长,则表明您分配了太多存活时间足以超过 gen 0 和 1 的对象,并且以重复的方式执行此操作。如果您的应用程序的内存使用量没有随着时间的推移而上升,这表明这些对象实际上是短暂的,但寿命足够长,可以在 0 和 1 集合中存活。这是一个糟糕的情况 - 您分配了一个短暂的对象,但支付了完整的第 2 代收集成本来清理它。

    如果是这样,你有几个不同的方向可以采取:

    1. 尝试让短寿命对象更快地被收集(这样它们就不会进入第 2 代,因此 GC 成本会更低)
    2. 尝试分配更少的短期对象(这样 GC 发生的频率会降低,并且在分配强制 GC 并将对象移至旧代之前,您有更多时间来完成使用短期对象)
    3. 使用堆栈分配的值类型而不是短期对象的引用类型(如果它适合您的目的)
    4. 如果您知道需要大量此类对象,请预先将它们汇集起来。听起来您正在这样做,但仍然必须进行大量分配才能将 GC 保持在 45%。如果您的池不够大,请预先分配更多 - 正如您所说,您有足够的备用内存。

    将所有这些结合起来可能是一个很好的解决方案。您需要很好地了解要分配的对象、它们的寿命以及它们实际需要多长时间才能实现您的目的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      • 2012-03-21
      • 2012-02-04
      • 2013-12-03
      • 1970-01-01
      • 2015-04-12
      相关资源
      最近更新 更多