【问题标题】:How to Tune the Java GC for vanilla benchmarks?如何为 vanilla 基准测试调整 Java GC?
【发布时间】:2012-05-16 07:04:00
【问题描述】:

我正在尝试调整 GC,使其在短期基准测试期间不会启动。

为此,我尝试将年轻一代设置得尽可能大,并确保我创建的垃圾少于 Eden 大小。

我使用了以下参数:

java -Xmx1g -Xms1g -Xmn1g -XX:NewSize=1000m -XX:MaxNewSize=1g -XX:SurvivorRatio=10000 -verbose:gc Main

但由于某种原因,当 VM 内存仅为 300M 时,我仍然会看到 [Full GC] 日志,是否缺少任何 JVM 调整?

【问题讨论】:

  • 不看你的代码真的很难说。也许您可以想出一个简短的可运行示例(出乎意料地)表现出这种行为,并将其发布在这里?这将使我们能够对其进行试验,并了解到底发生了什么。
  • 谢谢,我会尝试创建一个。这是否意味着总体上您认为我可以实现预期目标?
  • 原则上可以(前提是您的代码不会产生足够的垃圾来要求收集)。
  • 正如我在问题中所写,我看到完整的 GC 在 300M 时启动,而我有 1G。
  • 这些是我们看到的 GC 日志:[Full GC 444212K->53612K(1048512K), 0.1674871 secs] [Full GC 98878K->53600K(1048512K), 0.1353826 secs] [Full GC 117736K-> 53600K(1048512K),0.1126983秒] [全GC 96769K-> 53600K(1048512K),0.1046997秒] [全GC 202470K-> 53600K(1048512K),0.1597075秒] [全GC 56930K-> 53600K(1048512K),0.1134397秒] [全GC 267471K-> 53756K(1048512K),0.1372057秒] [全GC 54990K-> 53756K(1048512K),0.1068497秒] [全GC 160691K-> 53912K(1048512K),0.1366148秒] [全GC 122243K-> 53913K( 1048512K), 0.1218144 秒] [完整 GC 360042K->53922K(1048512K), 0.1614036 秒]

标签: java performance garbage-collection jvm benchmarking


【解决方案1】:

你可以获得full GC,因为你的tenured空间已经被填满了(大对象被直接放入tenured空间)

我通常尝试 4 - 24 GB 的 eden 大小,但要确保永久空间大小为 0.5 到 2 GB。

【讨论】:

    【解决方案2】:

    你的幸存者比率非常重要,10 000 你告诉 JVM 幸存者空间是伊甸园大小的 10000 分之一。

    From documentation

    如果幸存者空间太小,复制集合会直接溢出到终身代。如果幸存者空间太大,它们将是无用的空。在每次垃圾回收时,虚拟机选择一个对象在其被永久保存之前可以被复制的阈值次数。选择此阈值以使幸存者保持半满。命令行选项 -XX:+PrintTenuringDistribution 可用于显示此阈值和新生代对象的年龄。它对于观察应用程序的生命周期分布也很有用。

    很明显,由于幸存者空间比率如此之低,您的对象直接存储在终身代中。

    当终身代满时,主要收集发生了,这就是您在日志中看到 [Full GC...] 的原因。

    如果你想让年轻一代发挥更大的作用:

    -XX:NewRatio=1
    

    这意味着伊甸园和幸存者空间的组合大小将是总堆大小的一半。 (我猜你不可能有更大的年轻一代)

    如果你已经设置了-Xmn,你也不必设置-XX:NewSize-Xmn从1.4开始也是一样的。而且我猜你不想从上面绑定年轻一代与-XX:MaxNewSize,默认值是无限的。但是将年轻代的大小设置为与最大堆大小相同的值意味着您不会为老年代留出空间,所以我猜JVM会调整代的大小。

    总而言之,在某些情况下,分配可以直接发生在终身代中。 (Do objects ever get allocated directly into the old generation?)

    • 如果在年轻代分配失败并且对象是一个不包含任何对象引用的大数组,则可以直接将其分配到年老代。在某些特定情况下,此策略旨在通过从老年代分配来避免收集年轻代。 阈值大小为 64k 字。

    【讨论】:

    • 我希望伊甸园如此之大,永远不会被收集。因此 GC 永远不会尝试将对象放入生存区。
    • @GuyKorland 我进行了编辑。我希望这些信息会有所帮助;)
    • 感谢上一版,有没有办法控制“阈值大小为64k字。”?
    • 实际上 64k 大小取决于您的 JVM 版本和您使用的收集器。如果您阅读第二个链接中给出的完整答案,您会看到此阈值已提高到年轻一代的大小,并且可以设置阈值的选项已默认设置为无限制。所以我想你对此无能为力。只要给你的年轻代一个正确的大小,避免超过堆大小的一半。
    【解决方案3】:

    如果你要释放大量对象,GC 仍然会运行,这不仅仅是等到你用完所有分配的空间的问题,如果这样做的话,有时会导致严重的 GC 抖动它确实运行了。

    您应该查看您对对象处理的使用以及是否可以提高效率。查看 LMAX 破坏者模式,了解如何通过更新对象值而不是解除分配替换对象本身来重用对象。

    话虽如此,这是一篇关于调整 GC 的非常好的文章 - http://developer.amd.com/documentation/articles/pages/4EasyWaystodoJavaGarbageCollectionTuning.aspx

    【讨论】:

    • 我知道,但我真的不想在生产中使用这些参数运行,而是要将 GC 暂停与应用程序逻辑隔离开来。
    • 在这种情况下,更有理由关注您如何处理对象。如果你有大量的释放,你将有高 GC。您的应用程序中如何引用对象?您是否在每次需要时都创建处理类而不是重复使用它们?如果问题是您有大量新数据进入应用程序,您只需要在短时间内创建一组您永远不会释放但用于保存进入应用程序的原始值的对象并不断重复使用它们。
    • 您应该让 GC 使用其应用程序设置,并像往常一样平均运行时 - 这将考虑到由被基准测试的代码创建的平均 GC 负载。
    • 我们在基准测试中这样做,并且可以肯定这是衡量事物的唯一真正方法。然而,对于我的问题,您知道以这种方式设置 GC 的方法吗?
    猜你喜欢
    • 2010-09-20
    • 1970-01-01
    • 2014-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    相关资源
    最近更新 更多