CMS收集器无法跟上Old Gen的步伐

时间:2015-07-30 08:32:46

标签: java garbage-collection concurrent-mark-sweep

在一个中等繁忙的生产服务器上(50个应用程序线程,30%的CPU利用率),我们发现CMS收集器没有跟上升级到旧一代的对象的情况。

我最初的想法是这些对象显然仍然被引用,因此不适合收集 - 但是当Old Gen填充并提示连续收集时,6 GiB中的5.5 GiB被恢复。

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

由于旧版本中的对象有资格收集,而且服务器有足够的(明显的)资源,我不明白为什么CMS收集器不能保持旧的gen大小:

Visual VM

可能导致此情况的原因是什么?

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

替代

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

在理想的世界中,我们能够尝试使用G1收集器,但由于各种原因,我们仍然使用Java 6 JVM。

1 个答案:

答案 0 :(得分:1)

当您说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%为您提供大约500 MB的净空,这意味着CMS必须在不到5分钟的时间内赢得比赛。除非...

...除了我们在图表中看到的流畅的流量配置文件之外,您在幕后发生了一些事情。例如,您是否有任何后台进程刷新内存数据结构,如缓存?这些类型的活动会产生突然的,大量的新的,长寿的物体,需要被提升为旧的。它会使您的平滑图形突然变为垂直,并且可以快速耗尽可用内存。 CMS收集器擅长处理流畅,稳定的流量,但它很容易受到快速爆发的活动的影响。它很好地响应你的垃圾发生率的逐渐变化,但它无法预测"突发"行为,我看到很多像这样的案子导致它失去了比赛。

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

http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#cms.starting_a_cycle

另外,请关注您的PermGen空间。与并行吞吐量收集器不同,CMS收集器默认情况下不会收集PermGen,因此如果它已经填满,您最终会得到一个全世界的完整GC。此参数使CMS收集器也收集PermGen空间:CMSClassUnloadingEnabled。

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

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