13

我在使用 Concurrent Mark-Sweep 收集器的应用程序的 GC 日志文件中看到以下症状:

4031.248: [CMS-concurrent-preclean-start]
4031.250: [CMS-concurrent-preclean: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
4031.250: [CMS-concurrent-abortable-preclean-start]
 CMS: abort preclean due to time 4036.346: [CMS-concurrent-abortable-preclean: 0.159/5.096 secs] [Times: user=0.00 sys=0.01, real=5.09 secs] 
4036.346: [GC[YG occupancy: 55964 K (118016 K)]4036.347: [Rescan (parallel) , 0.0641200 secs]4036.411: [weak refs processing, 0.0001300 secs]4036.411: [class unloading, 0.0041590 secs]4036.415: [scrub symbol & string tables, 0.0053220 secs] [1 CMS-remark: 16015K(393216K)] 71979K(511232K), 0.0746640 secs] [Times: user=0.08 sys=0.00, real=0.08 secs] 

预清洁过程不断中止。我尝试将 CMSMaxAbortablePrecleanTime 从默认值 5 调整为 15 秒,但这并没有帮助。当前的JVM选项如下...

Djava.awt.headless=true
 -Xms512m
 -Xmx512m
 -Xmn128m
 -XX:MaxPermSize=128m
 -XX:+HeapDumpOnOutOfMemoryError
 -XX:+UseParNewGC
 -XX:+UseConcMarkSweepGC
 -XX:BiasedLockingStartupDelay=0
 -XX:+DoEscapeAnalysis
 -XX:+UseBiasedLocking
 -XX:+EliminateLocks
 -XX:+CMSParallelRemarkEnabled
 -verbose:gc
 -XX:+PrintGCTimeStamps
 -XX:+PrintGCDetails
 -XX:+PrintHeapAtGC
 -Xloggc:gc.log
 -XX:+CMSClassUnloadingEnabled
 -XX:+CMSPermGenPrecleaningEnabled
 -XX:CMSInitiatingOccupancyFraction=50
 -XX:ReservedCodeCacheSize=64m
 -Dnetworkaddress.cache.ttl=30
 -Xss128k

似乎并发中止预清理永远没有机会运行。我阅读了https://blogs.oracle.com/jonthecollector/entry/did_you_know,其中建议启用 CMSScavengeBeforeRemark,但暂停的副作用似乎并不理想。有人可以提供任何建议吗?

另外我想知道是否有人对了解 CMS GC 日志有很好的参考,特别是这一行:

[1 CMS-remark: 16015K(393216K)] 71979K(511232K), 0.0746640 secs]

不清楚这些数字指的是哪些内存区域。 编辑 找到了这个http://www.sun.com/bigadmin/content/submitted/cms_gc_logs.jsp的链接

4

4 回答 4

3

正如有人已经提到的,第一步是增加 CMSInitiatingOccupancyFraction。

作为第二步,我将使用标志-XX:-PrintTenuringDistribution并确保不会过早地从年轻一代提升到老一代。这会导致从旧到新的引用,这可能会导致更长的可中止预清理阶段。如果有这样的过早提升,尝试调整伊甸园和幸存者空间的比例。

于 2012-02-16T21:45:13.677 回答
3

[时间:用户=0.00 系统=0.01,真实=5.09 秒]

我会尝试调查为什么CMS-concurrent-abortable-preclean-start在 5 秒内既没有得到用户也没有得到 sys CPU 时间。

我的建议是从一个“干净的”JVM CMS 启动标志开始,例如

-Djava.awt.headless=true
-Xms512m
-Xmx512m
-Xmn128m
-Xss128k
-XX:MaxPermSize=128m
-XX:+UseConcMarkSweepGC
-XX:+HeapDumpOnOutOfMemoryError
-Xloggc:gc.log
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC

然后检查问题是否重现并继续一次调整一个参数。

于 2010-06-24T10:12:27.437 回答
2

关于这种现象,这里有一个很好的解释:

引用:

所以当系统负载较轻时(也就是说不会有minor gc),precleaning总会超时,full gc总会失败。cpu是废物。

它不会失败。它的并行性会降低(即效率会降低,并且会有更长的暂停时间,以减少工作量)。

所以总而言之:这似乎是正常的操作——线程只是等待一次次要 GC 发生 5 秒,但是当这没有发生时没有什么大问题:JVM 选择了不同的(效率较低的)策略继续与 GC。

于 2014-10-03T09:24:36.340 回答
0

对于我正在使用的服务,我添加了:

-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80

这将 JVM 配置为仅在 80% 已满后才开始标记,值得一试。

于 2021-12-23T12:11:05.300 回答