我最近将我的Java应用程序从CMS + ParNew切换到了G1GC。 我在切换时观察到的是CPU使用率更高,GC计数+暂停时间也增加了。 切换前的JVM标志是
java -Xmx22467m -Xms22467m -XX:NewSize=11233m -XX:+UseConcMarkSweepGC -XX:AutoBoxCacheMax=1048576 -jar my-application.jar
切换后我的标志是:
java -Xmx22467m -Xms22467m -XX:+G1GC -XX:AutoBoxCacheMax=1048576 -XX:MaxGCPauseMillis=30 -jar my-application.jar
我遵循了Oracle的最佳实践http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
Do not Set Young Generation Size
并没有设定年轻一代的规模。 但是我怀疑年轻一代是这里的问题。 我看到的是堆使用量在~6 - 8 GB之间波动。
之前,使用CMS和Par New,内存使用量增长到4-16 GB之间,然后才看到GC:
我不确定我理解为什么使用G1GC GC会如此频繁。我不确定在使用G1GC进行GC调整时我缺少什么。
我正在使用Java 8: ava版“1.8.0_144” Java(TM)SE运行时环境(版本1.8.0_144-b01) Java HotSpot(TM)64位服务器VM(版本25.144-b01,混合模式)
感谢您的帮助。
更新: 关于这些暂停的更多信息: 你可以看到所有这些停顿都是G1New,看起来它们和我的目标暂停时间一样长,即30ms。 当我在切换到G1GC之前查看ParNew暂停时,这就是它的样子: 因此它们也都是年轻的gen集合(ParNew),但它们不那么频繁和更短,因为它们只在堆使用量达到14GB左右时发生(根据图表)
我仍然很清楚为什么G1New如此早发生(就堆使用而言)
更新2 我也注意到NewRatio = 2,我不知道G1GC是否尊重这一点,但这意味着我的New Gen的上限为7GB。这可能是原因吗?
更新3 添加G1GC GC日志: https://drive.google.com/file/d/1iWqZCbB-nU6k_0-AQdvb6vaBSYbkQcqn/view?usp=sharing
答案 0 :(得分:2)
GC日志显示平均GC暂停间隔为2秒,每次间隔约为30-40毫秒,相当于应用程序吞吐量约为95%。这并不等于"杀死性能"领土。至少不是由于GC暂停。
G1会做更多的并发工作,例如为了记住集合细化而你的暂停似乎花了一些时间在update/scan RS,所以我假设并发GC线程也很繁忙,即它可能需要在GC暂停之外的额外CPU周期,默认情况下不会被日志覆盖,你需要+G1SummarizeRSetStats
。如果延迟更重要,您可能希望为机器分配更多内核,如果吞吐量更重要,您可以调整G1以在暂停期间执行更多RS更新(以增加暂停时间为代价)。
答案 1 :(得分:2)
我能够看到复制对象所花费的时间非常重要。在对象被提升为Tenured Generation之前,看起来G1GC默认有15代。
我把它减少到1(-XX:MaxTenuringThreshold=1
)
此外,我不知道如何在日志中确认它,但是可视化GC日志我看到年轻一代正在不断调整大小,从最小尺寸到最大尺寸。我缩小了范围,也提高了性能。
在这里https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector-tuning.htm#JSGCT-GUID-70E3F150-B68E-4787-BBF1-F91315AC9AB9 我试图弄清楚粗糙是否确实是一个问题。但它只是说设置gc + remset = trace我不明白如何在命令行中传递给java,如果它甚至可以在JDK 8中使用。 为了以防万一,我增加了XX:G1RSetRegionEntries。
我希望它对未来的G1GC调谐器有所帮助,如果其他人有更多的建议会很棒。
我仍然看到[处理缓冲区]在年轻人疏散中仍然需要很长时间,并且[Scan RS]在混合收藏中很长。 不确定为什么