几天之后,GC暂停时间变得非常长。

7
我正在运行一个构建系统。我们过去使用CMS收集器,但是我们开始遭受非常长的full GC周期的困扰,吞吐量(不进行GC的时间)约为90%。因此,我现在决定切换到G1,并假设即使我有更长的总GC时间,暂停时间也会更短,从而确保更高的可用性。所以这个想法似乎比我预期的要好,我几乎3天没有看到任何full GC,吞吐量为97%,整体GC性能要好得多。(所有截图和数据都来自GCViewerNormal 直到现在(第6天)。今天系统变得非常疯狂。旧空间利用率仅略低于100%。我几乎每2-3分钟就会看到Full GC被触发: Berzerk! 旧空间利用率: Old space 堆大小为20G(总共128G RAM)。我当前使用的标志是:
-XX:+UseG1GC
-XX:MaxPermSize=512m
-XX:MaxGCPauseMillis=800
-XX:GCPauseIntervalMillis=8000 
-XX:NewRatio=4
-XX:PermSize=256m
-XX:InitiatingHeapOccupancyPercent=35
-XX:+ParallelRefProcEnabled

日志记录标志。 我似乎缺少的是 -XX:+ParallelGCThreads=20(我有32个处理器),默认应该是8个。 我还从Oracle那里了解到,建议为20G堆使用-XX:+G1NewSizePercent=4,默认值应该为5。

我正在使用Java HotSpot(TM) 64-Bit Server VM 1.7.0_76,Oracle Corporation。

你有什么建议吗? 我有明显的错误吗? 该怎么改变? 如果只给Java 20G,我的需求是否过于贪心?这里的假设是如果分配太多堆,则意味着有更多东西要清理,从而导致更长的GC时间(平民逻辑)。

PS:应用程序不是我的。 对我来说,它是一个盒装产品。


5
我认为你的软件中存在一些内存泄漏,这将逐渐消耗可用堆空间,使得垃圾回收随着时间的推移变得越来越困难。因此,解决方案不应该在于搜索GC算法或堆设置(无论大小如何,它最终都会填满)。你必须修复你的软件,或者接受你需要时不时重启它的事实。有趣的是,看起来你的堆不会完全填满并崩溃你的程序,所以也许我错了。 - Giulio Franco
你应该使用-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy -Xloggc:<logfile>参数打印GC日志,并将其粘贴到Pastebin上,这样更有助于理解G1的决策。如果可以的话,建议尝试Java 8,因为自那以后,G1GC已经经历了很多变化。它的许多启发式算法得到了改进,一些瓶颈也被消除了。据我所知,在7中有一些情况下,G1可能会陷入困境。 - the8472
我找到了一个解决方案,该系统还允许用户在构建过程中执行自定义脚本。经过(非常长时间的)调查,发现一个用户不断地执行一个未释放内存的脚本,导致堆基线稳步上升,因此每次GC循环释放的内存越来越少。 - Erki M.
1个回答

1
你有什么建议?我有明显的错误吗?需要改变什么?只给Java分配20G是贪心的吗?这里的假设是,如果给它太多堆空间,那么GC时间会更长,因为需要清理的东西更多(平民逻辑)。
如果它触发了full GC但占用率保持在那20GB附近,那么可能是GC没有足够的空间,无论是为了满足大量分配的需求还是为了实现一些目标(吞吐量、暂停时间),都强制进行full GC作为后备方案。
所以你可以尝试增加堆限制或放宽吞吐量目标。
正如我之前评论中提到的,你也可以尝试升级到Java8以改善G1启发式算法。
进一步建议,覆盖“狂暴”行为的GC日志将非常有用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接