我正在使用CMS进行GC,但每两个月使用一次CPU会非常高。
当情况变得更糟时,有一条GC日志,你可能会发现长STW。
3519696.386: [GC [1 CMS-initial-mark: 8617524K(12582912K)] 17105967K(23907584K), 4.9369140 secs] [Times: user=4.94 sys=0.00, real=4.94 secs]
3519701.324: [CMS-concurrent-mark-start]
3519709.419: [CMS-concurrent-mark: 8.096/8.096 secs] [Times: user=16.17 sys=0.00, real=8.09 secs]
3519709.420: [CMS-concurrent-preclean-start]
3519709.442: [CMS-concurrent-preclean: 0.023/0.023 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
3519709.442: [CMS-concurrent-abortable-preclean-start]
CMS: abort preclean due to time 3519714.691: [CMS-concurrent-abortable-preclean: 3.345/5.248 secs] [Times: user=3.36 sys=0.00, real=5.25 secs]
3519714.692: [GC[YG occupancy: 8489655 K (11324672 K)]3519714.692: [Rescan (parallel) , 8.4072250 secs]3519723.099: [weak refs processing, 0.0000190 secs]3519723.099: [scrub string table, 0.0008130 secs] [1 CMS-remark: 8617524K(12582912K)] 17107180K(23907584K), 8.4081940 secs] [Times: user=65.71 sys=0.15, real=8.41 secs]
3519723.100: [CMS-concurrent-sweep-start]
3519725.451: [CMS-concurrent-sweep: 2.350/2.350 secs] [Times: user=2.36 sys=0.00, real=2.35 secs]
3519725.451: [CMS-concurrent-reset-start]
3519725.478: [CMS-concurrent-reset: 0.028/0.028 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
3519727.480: [GC [1 CMS-initial-mark: 8617522K(12582912K)] 17107229K(23907584K), 4.9378950 secs] [Times: user=4.94 sys=0.00, real=4.94 secs]
3519732.418: [CMS-concurrent-mark-start]
我的GC参数:
java -server -Xmx24g -Xms24g -XX:NewSize=12g -XX:MaxNewSize=12g -XX:+HeapDumpOnOutOfMemoryError -XX:MaxDirectMemorySize=24g -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:ReservedCodeCacheSize=128m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:CMSInitiatingOccupancyFraction=68
我的服务器上安装了9个CPU和64G内存。
你能帮忙找出每月变得更糟的关键点吗?
答案 0 :(得分:4)
好的,让我们看一下细节。我首先要注意的是,所有时间似乎都在用户中,而不是在sys中,所以主要的嫌疑人是JVM和应用程序。
GC是在老一代触发的。入住容量为8617524K,容量为12582912K。总堆使用量为17105967K,大小为23907584K。
初始标记需要约5秒。
3519696.386: [GC [1 CMS-initial-mark: 8617524K(12582912K)] 17105967K(23907584K), 4.9369140 secs] [Times: user=4.94 sys=0.00, real=4.94 secs]
AFAIK初始标记仅处理GC根。你可以看到这些here是哪一个,但它吸收了这么多的事实很奇怪。我首先怀疑这会受到安全点时间的影响,所以可能启用:
-XX:+ PrintSafepointStatistics -XX:PrintSafepointStatisticsCount = 1
并发标记阶段需要8s
3519709.419: [CMS-concurrent-mark: 8.096/8.096 secs] [Times: user=16.17 sys=0.00, real=8.09 secs]
这是扫描实时对象
Preclean相对较快。
Abortable preclean在5s时取消,AFAIK可以使用CMSMaxAbortablePrecleanTime进行配置。挖掘这个选项,我发现在这个阶段有很小的集合是可取的,如果不这样做可能会导致CMS中出现大量的暂停。增加CMSMaxAbortablePrecleanTime,并激活CMSScavengeBeforeRemark。请Jon Masamitsu查看此帖子。
年轻一代是8G,重新扫描需要8s,这似乎太多了。同样的评论重新。 safepoints。
3519714.692:[GC [YG占有率:8489655 K(11324672K)] 3519714.692:[Rescan(parallel),8.4072250 secs] 3519723.099:[weak refs processing,0.0000190 secs] 3519723.099:[scrub string table,0.0008130 secs] [ 1 CMS-remark:8617524K(12582912K)] 17107180K(23907584K),8.4081940 secs] [次:用户= 65.71 sys = 0.15,real = 8.41 secs]
注意年轻的一代。在此期间,规模实际上增加了:8617524K
并发扫描的最终清理需要2.35秒,并且堆内容似乎没有显着变化。你仍然有大致相同的年轻和堆使用。
总结我看到两点:
有关您的应用等的更多详细信息肯定有助于更好地确定它。我希望有所帮助。
答案 1 :(得分:1)
在您的日志中,我看不到正常的年轻GC。 CMS Stop-the-World阶段旨在让年轻的空间收集更有效。
3519727.480: [GC [1 CMS-initial-mark: 8617522K(12582912K)] 17107229K(23907584K), 4.9378950 secs] [Times: user=4.94 sys=0.00, real=4.94 secs]
这里CMS-initial-mark
必须在一个线程中扫描8.6 GiB的年轻空间。如果在年轻的GC之后立即调用年轻的太空占领将会小一些。
同样适用于CMS-remark
。
年轻人有很大的年轻空间,所以当你的旧空间被更频繁地收集到那个年轻的空间时,你就会陷入困境。
以下几种方法可以解决这个问题
-XX:CMSWaitDuration=3600000
让CMS-initial-mark
等到一个小时,直到下一个年轻的GC。-XX:+CMSScavengeBeforeRemark
让CMS-remark
强制年轻人收集产生可预测的暂停时间。CMS-initial-mark
this article中提供了更多详细信息。