首页 > 解决方案 > 为什么 Java CMS 垃圾收集器不允许已用堆大小增长到可用堆大小?

问题描述

我正在测试 G1GC 和 CMS 垃圾收集器之间的差异。运行相同的程序会产生不同的堆大小使用(可能如预期)。

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

在此处输入图像描述

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

标签: javagarbage-collectiong1gc

解决方案


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

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

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

你能为这个做什么?

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

  2. 您可以更改减小 Eden 空间的大小:具体调整 NewSize 和 MaxNewSize。然而,这对于正常的工作负载来说是不利的。这很容易导致年轻对象过早地被长期使用,这将增加 CMS 收集器的负载。

  3. 有一些较新的收集器旨在更好地处理“完整”堆。


推荐阅读