java - CMS 收集器跟不上 Old Gen 的步伐

标签 java garbage-collection concurrent-mark-sweep

在一个中等繁忙的生产服务器上(50 个应用程序线程,30% 的 CPU 使用率),我们看到 CMS 收集器跟不上提升到老年代的对象的情况。

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

Eden 空间的大小为 3 GiB,大约需要 20-30 秒才能填满以启动新的收集。 Survivor 空间使用量在 800 - 1250 MiB 之间波动,最大为 1.5 GiB(每个)。

老一代中的对象符合收集条件,并且服务器拥有大量(明显的)资源,我不明白为什么 CMS 收集器不能保持在老一代大小之上:

Visual VM

是什么导致了这种情况,有什么解决办法吗?

我知道占用率,但我不明白 CMSIncrementalSafetyFactor 的含义 - 我读过一些 Oracle 文档,但我不知道“添加[ing ] 计算占空比时的保守主义”实际上意味着..?

备选方案

切换到并行/吞吐量收集器会产生非常低的 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% 为您提供了大约 500 MB 的空间,这意味着 CMS 必须在不到 5 分钟的时间内赢得比赛,它会的。除非……

...除了我们在图表中看到的流畅流量配置文件之外,您在幕后发生了一些事情。例如,您是否有任何后台进程来刷新内存中的数据结构(如缓存)?这些类型的 Activity 会突然产生大量新的、长期存在的对象,这些对象需要提升到老年代。它会使您的平滑图形突然垂直,并且会很快耗尽可用内存。 CMS 收集器擅长处理平稳、稳定的流量,但它很容易受到快速 Activity 的影响。它擅长响应垃圾生成率的逐渐变化,但它无法预测“突发”行为,我见过很多这样导致它输掉比赛的案例。

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

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

此外,还要注意您的 PermGen 空间。与并行吞吐量收集器不同,CMS 收集器默认情况下不收集 PermGen,因此如果它被填满,您最终会遇到一个 stop-the-world full GC。此参数使 CMS 收集器也收集 PermGen 空间:CMSClassUnloadingEnabled。

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

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

关于java - CMS 收集器跟不上 Old Gen 的步伐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31718844/

相关文章:

java - 奇怪的线程情况——仅使用线程来实例化变量

Python 对象没有引用者但仍可通过弱引用访问?

c++ - 混合模式进程与托管到非托管 IPC

java - 100%老一代——这是一个问题吗?

performance - 如何记录和绘制 Java 应用程序的对象生命周期?

Java ConcurrentMarkSweep 垃圾收集器不清除所有垃圾

java - 我应该如何针对我的应用程序调整 CMS?

java - Java 代码可以判断它是否在应用服务器中吗?

java - HTTP 500 网站无法显示页面

java - 如何将参数传递给匿名类?