Java CMS收集器无法跟上旧代的步伐

Java CMS收集器无法跟上旧代的步伐,java,garbage-collection,concurrent-mark-sweep,Java,Garbage Collection,Concurrent Mark Sweep,在中等繁忙的生产服务器上(50个应用程序线程,30%的CPU利用率),我们看到CMS收集器无法跟上升级到旧一代的对象的速度 我最初的想法是,这些对象显然仍然被引用,因此不符合收集条件-但当旧Gen填充并提示进行一次串行收集时,6 GiB中的5.5 GiB被恢复 伊甸园的空间大小为3 GiB,大约需要20-30秒来填满足够的空间,以提示年轻的收藏。幸存者空间的使用量在800-1250 MiB之间波动,每个最大1.5 GiB 由于旧版本中的对象符合收集条件,并且服务器拥有大量(明显的)资源,我不明白

在中等繁忙的生产服务器上(50个应用程序线程,30%的CPU利用率),我们看到CMS收集器无法跟上升级到旧一代的对象的速度

我最初的想法是,这些对象显然仍然被引用,因此不符合收集条件-但当旧Gen填充并提示进行一次串行收集时,6 GiB中的5.5 GiB被恢复

伊甸园的空间大小为3 GiB,大约需要20-30秒来填满足够的空间,以提示年轻的收藏。幸存者空间的使用量在800-1250 MiB之间波动,每个最大1.5 GiB

由于旧版本中的对象符合收集条件,并且服务器拥有大量(明显的)资源,我不明白为什么CMS收集器不能保持旧版本的大小:

是什么导致了这种情况?有什么解决方案吗?

我知道占用率,但我不理解
CMSIncrementalSafetyFactor
的含义-我已经阅读了一些Oracle文档,但我不知道“在计算占空比时添加保守性”实际上意味着什么

备选方案

切换到并行/吞吐量收集器会产生非常低的GC开销(1.8%),但偶尔会有(每天50次)长时间的暂停—每个完整GC大约20秒。即使进行了一些调整,也不太可能达到我们的最大暂停目标


在理想情况下,我们可以试验G1收集器,但由于各种原因,我们只能使用Java 6 JVM。

当您说CMS收集器无法跟上对象升级速度时,这意味着您应该在GC日志中看到“并发模式失败”。这些是当CMS收集器“失去竞争”并且在完成之前内存不足时得到的

2014-02-27T01:09:52.408-0600: 847.004: [GC 847.005: [ParNew 
(promotion failed)
Desired survivor size 78512128 bytes, new threshold 2 (max 15)
- age   1:   60284680 bytes,   60284680 total
- age   2:   32342648 bytes,   92627328 total
: 1380096K->1380096K(1380096K), 0.7375510 secs]847.743: 
[CMS2014-02-27T01:09:54.133-0600: 848.729: [CMS-concurrent-s
weep: 5.467/6.765 secs] [Times: user=21.59 sys=0.73, real=6.76 
secs]
  (concurrent mode failure): 2363866K->1763900K(4409856K),
10.6658960 secs] 3697627K->1763900K(5789952K), [CMS Perm : 
118666K->117980K(125596K)], 11.4061610 secs] 
[Times: user=11.34 sys=0.02, real=11.57 secs]
默认情况下,CMS收集器将在旧一代的92%占用率下触发。根据旧一代使用情况图表中的内存增长率判断,您的内存每5分钟增长约500 MB。6GB的92%为您提供了大约500MB的净空空间,这意味着CMS必须在不到5分钟的时间内赢得这场比赛,这是必然的。除非

…除了我们在图表中看到的流畅的交通状况之外,还有一些事情发生在幕后。例如,您是否有任何后台进程可以刷新内存中的数据结构(如缓存)?这些类型的活动会突然创建大量新的、长寿命的对象,需要升级到旧版本。这会使平滑的图形突然垂直,并且会很快耗尽可用内存。CMS收集器擅长处理平稳、稳定的流量,但它非常容易受到快速突发活动的影响。它能够很好地响应垃圾生成率的逐渐变化,但它无法预测“突发”行为,我见过许多这样的情况导致它输掉比赛

除了完全避免产生突然爆发的新对象的后台进程外,您还可以通过将cmsinitiatingOccupencyFraction参数降低到60-80(而不是默认的92%)之间的某个值,为CMS收集器提供一个良好的开端

另外,也要注意你的永久基因空间。与并行吞吐量收集器不同,CMS收集器在默认情况下不收集PermGen,因此,如果PermGen填满,您将以停止world full GC结束。此参数使CMS收集器也收集永久空间:CMSClassUnloadingEnabled

除此之外,我建议打开GC日志记录和设置: -XX:+PrintGCDetails打印每个次要和主要垃圾收集的详细信息

这是一个很好的参数,可以让您在启动时查看每个JVM设置:
-XX:+PrintFlagsFinal在启动时打印所有JVM配置选项的值

查看GC日志事件的输出可以让您更深入地了解。@MarkoTopolnik是否有任何我应该查找的特定事件?我已经查看了日志,但没有任何异常(在我看来)。我希望所有CMS相变事件的时间安排和实际收集的数量应该比现在的问题更清楚。