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

3

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

下面的图像显示了左侧为G1GC,右侧为CMS GC。G1GC成功运行整个程序,而当使用CMS时,会引发outOfMemoryException

enter image description here

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

1个回答

2
简短的回答是,当堆填满时,CMS管理内存的能力不如G1GC。这也是CMS正在逐步被淘汰的原因之一。
稍微详细点说,CMS中保留的2GB未使用空间是为了在Eden空间的minor(复制)GC中转移对象。相比之下,G1GC似乎能够调整(缩小)Eden空间以适应Tenured空间的填充。
请注意,这实际上可能是一个不现实的基准测试结果。在典型的应用程序中,minor collections将成功删除大多数新对象。在您的基准测试中,似乎几乎所有东西都仍然可达,因此分配的大部分对象最终都进入了Tenured空间。
你可以怎么做呢?
1. 切换到G1GC。在大多数情况下,它比CMS更好。(最终您将没有选择。CMS已在Java 9中被弃用,并已在Java 14中被删除。)
2. 您可以减小Eden空间的大小:具体来说是调整NewSize和MaxNewSize。但是,对于正常工作负载,这有劣势。它容易导致年轻对象过早地进入Tenured空间,从而增加CMS收集器的负载。
3. 还有一些新的收集器,旨在更好地处理“满”堆。

只有当您禁用G1的自适应策略时,MaxNewSizeNewSize才有意义,但这很少是您想要做的事情。 - Eugene
@Eugene - 我在谈论CMS案例。(虽然我不是说这是一个好主意...) - Stephen C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接