旧版“空”时运行旧版垃圾收集的JVM

时间:2015-12-10 21:12:54

标签: java clojure garbage-collection jvm

我运行的Clojure应用程序有大量内存流失用于大堆分配,因此它已适当地设置JVM opts:

-Xmx13g -XX:+UseConcMarkSweepGC -XX:NewSize=10G -server -XX:+UseParNewGC

这大部分时间都可以工作,并且可以避免新一代溢出到旧版本的问题(有时候仍会让它成为幸存者,但并非总是如此),但有时候我们会看到如图所示的情况,即JVM运行CMS /旧当新一代充满时,垃圾收集真的很难。但是,我的理解是,这应该由新一代垃圾收集器处理。

enter image description here

两个问题。为什么旧的垃圾收集器运行时,旧的几乎是空的,但新的gen有东西?我是否可以进行任何进一步的调整,以减少GC不可避免地造成的暂停/减速?

ETA:在OpenJDK 8上运行。

ETA 2:GC日志:https://gist.github.com/gworley3/6abb9ab52320c6cbd508

1 个答案:

答案 0 :(得分:2)

简答:

CMS正在花费太多时间扫描近10GB的Young Gen,而它没有。您可以通过添加选项阻止CMS收集器不必要地运行:

‑XX:+UseCMSInitiatingOccupancyOnly ‑XX:CMSInitiatingOccupancyFraction=<K>

其中 K 是占用的旧Gen空间的任意部分。

长答案:

参考Oracle CMS Documentation

  

CMS收集器在并发收集周期中暂停两次应用程序。第一个暂停是将根目录中可直接访问的对象标记为实时(例如,来自应用程序线程堆栈和寄存器的对象引用,静态对象等)以及堆中的其他位置(例如,年轻代)。第一次暂停被称为初始标记暂停。第二个暂停在并发跟踪阶段结束时发现,并且在CMS收集器完成对该对象的跟踪之后,找到由于对象中引用的应用程序线程更新而导致的并发跟踪遗漏的对象。第二次暂停被称为备注暂停。

CMS正在进行两次 stop-the-world 暂停,每次扫描Young Gen进行标记。查看日志的前三行,您可以看到初始标记在ParNew之前需要5秒,之后需要0.25秒。这句话看到了类似的时间减少。

<190>1 2015-12-18T08:44:54.194216+00:00 host app web.1 - [GC[YG occupancy: 489826 K (9437184 K)][Rescan (parallel) , 0.2520950 secs][weak refs processing, 0.0000180 secs][scrub string table, 0.0008190 secs] [1 CMS-remark: 64504K(107240K)] 554330K(9544424K), 0.2530410 secs] [Times: user=1.95 sys=0.02, real=0.25 secs]

<190>1 2015-12-18T08:44:49.937257+00:00 host app web.1 - [GC[ParNew:   8418004K->93508K(9437184K), 0.0488580 secs] 8482343K->158013K(9544424K), 0.0489980 secs] [Times: user=0.33 sys=0.00, real=0.05 secs]

<190>1 2015-12-18T08:44:48.651086+00:00 host app web.1 - [GC [1 CMS-initial-mark: 64339K(107240K)] 7800216K(9544424K), 5.0756660 secs] [Times: user=5.08 sys=0.00, real=5.08 secs]

在每个CMS循环后,您的旧Gen空间几乎没有减少,因此开销当然不值得。减少CMS循环的频率将是一种可能的解决方案。