我正在测试G1GC和CMS垃圾收集器之间的差异。运行相同的程序会产生不同的堆大小使用情况(可能是预期的)。 下面的图像显示了左侧为G1GC,右侧为CMS GC。G1GC成功运行整个程序,而当使用CMS时,会引发outOfMemoryException。 因此,我的问题是:为什么CMS不允许已使用的堆大小达到可用的堆大小?当10GB可用时,堆大小在8.00GB处停止增长,并出现内存不足异常。
简短的回答是,当堆填满时,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. 还有一些新的收集器,旨在更好地处理“满”堆。
MaxNewSize
和NewSize
才有意义,但这很少是您想要做的事情。 - Eugene