在一个中等繁忙的生产服务器上(50个应用程序线程,30%的CPU利用率),我们发现CMS收集器没有跟上升级到旧一代的对象的情况。
我最初的想法是这些对象显然仍然被引用,因此不适合收集 - 但是当Old Gen填充并提示连续收集时,6 GiB中的5.5 GiB被恢复。
伊甸园空间的大小为3 GiB,需要大约20-30秒才能填满,以提示年轻人的收藏。幸存者空间使用量在800 - 1250 MiB之间波动,最大值为1.5 GiB(每个)。
由于旧版本中的对象有资格收集,而且服务器有足够的(明显的)资源,我不明白为什么CMS收集器不能保持旧的gen大小:
可能导致此情况的原因是什么?
我知道占用率,但我不了解CMSIncrementalSafetyFactor
的含义 - 我已经阅读了一些Oracle文档,但我不知道什么"在计算占空比时添加保守性"实际上意味着..?
替代
切换到并行/吞吐量收集器会产生非常低的GC开销(1.8%),但偶尔会出现(每天50次)长暂停 - 每个完整GC大约需要20秒。即使进行了一些调整,这也不可能达到我们的最大暂停目标。
在理想的世界中,我们能够尝试使用G1收集器,但由于各种原因,我们仍然使用Java 6 JVM。
答案 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配置选项的值