调整GC(CMS)的参数

时间:2016-07-26 04:04:38

标签: java garbage-collection jvm

我正在使用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内存。

你能帮忙找出每月变得更糟的关键点吗?

2 个答案:

答案 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秒,并且堆内容似乎没有显着变化。你仍然有大致相同的年轻和堆使用。

总结我看到两点:

  • 您的堆很大,您正在到达CMSInitiatingOccupancyFraction并触发CMS,并且很多时间似乎都在扫描实时对象。无论哪种方式,检查sizepoint时间以查看是否可以改进。
  • GC并没有真正收集太多,所以您可能处于以下某种情况:
    • 您希望保留大量长期存在的对象(例如:缓存)。在这种情况下,您希望增加CMSInitiatingOccuppancyFraction(因为您希望旧的gen变得非常满)。但也要注意你不要宣传任何中期或短期物品,因为这些物品最终(在一天或两个月内)会导致长期的GC。那就是:避免老一代的流失。
    • 您需要避免促销活动,因此您需要生成大量短期居住对象。减少分配,增加伊甸园。

有关您的应用等的更多详细信息肯定有助于更好地确定它。我希望有所帮助。

答案 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=3600000CMS-initial-mark等到一个小时,直到下一个年轻的GC。
  • -XX:+CMSScavengeBeforeRemarkCMS-remark强制年轻人收集产生可预测的暂停时间。
  • 使用具有并行CMS-initial-mark
  • 的最新Java 7/8

this article中提供了更多详细信息。

相关问题