为什么 Java CMS 垃圾收集器不允许使用的堆大小增长到可用的堆大小?

Why doesn't the Java CMS Garbage Collector allow the used heap size to grow to the available heap size?

我正在测试 G1GC 和 CMS 垃圾收集器之间的区别。 运行 相同的程序产生不同的堆大小使用情况(可能符合预期)。

下图显示了 G1GC(左)与 CMS GC(右)的对比。 G1GC 设法 运行 整个程序,而在使用 CMS 时,会引发 outOfMemoryException

因此,我的问题是:为什么 CMS 不允许已用堆大小达到可用堆大小?当 10GB 可用时,堆大小在 8.00GB 时停止增长并出现内存不足异常。

简短的回答是,当堆填满时,CMS 不如 G1GC 那样管理内存。这是 CMS 被淘汰的原因之一。

稍微长一点的答案是,CMS 中未使用的 2GB space 保留用于疏散 Eden space 的次要(复制)GC 中的对象。相比之下,G1GC 似乎能够在 Tenured space 填满时调整(缩小)Eden space。

请注意,这实际上可能是不切实际的基准测试的产物。在典型的应用程序中,次要集合将成功删除大部分新对象。在您的基准测试中,看起来几乎所有东西都可以访问,所以大多数分配的对象最终都在 Tenured space.

你能做些什么?

  1. 切换到 G1GC。在大多数情况下,CMS 更好。 (最终你别无选择。CMS 在 Java 9 中被弃用,并在 Java 14 中被删除。)

  2. 可以更改减小Eden的大小space:具体调整NewSize和MaxNewSize。但是,这对于正常工作负载有不利影响。容易导致年轻对象过早被使用,增加CMS收集器的负载。

  3. 有更新的收集器设计用于更好地处理“满”堆。