JVM 和 GC 调优 - 没有完整 GC 的理论

2022-09-04 21:44:31

我有一个具有两种类型对象的大规模应用程序:长生存期(缓存)和短生存期(请求 - 进程 - 响应)。从理论上讲,对于这种类型的应用程序,我认为可以配置年轻与旧的空间,因此旧的空间消耗是恒定的,导致没有完整的GC。

我已经更改了newSize-maxNewSize参数,但是,旧堆继续上升,直到完整GC。每个完整 GC 之后,消耗量降至 20%(缓存占用 20%)。出于某种原因,我的物品进入了旧空间。我有两个嫌疑人为什么要搬到旧空间:

  • 根据这篇文章:http://chaoticjava.com/posts/gc-tips-and-memory-leaks/,如果你分配了大物体,这些物体就会直接进入旧空间。这是真的吗,如果是,是否有JVM选项参数可以为Young空间设置对象大小阈值?

  • 如果我正确理解了这个过程,那么对象在移动到“旧”部分之前,会在“To-From”生存部分之间切换。是否有参数可以设置在移动到旧空间之前要在 To 和 From 之间完成多少次切换?

还有其他提示吗?

谢谢 阿玛尔


答案 1

这听起来确实像是你的幸存者空间不够大。您需要使它们足够大,以至于不需要收集任何对象。一个物体只被切换进出幸存者空间一次。

如果要分配大型对象,是否可以为它们使用对象池,而无需对它们进行 GGC 处理。您是否也考虑过对请求/流程/响应数据使用对象池?例如,一个简单的方法是使用ThreadLocal。

您是否尝试过G1收集器,该收集器旨在逐步收集所有内存并减少完整GC的大打击。


答案 2

您确定旧一代的增长不是缓存对象吗?除非您的缓存是固定的并且永远不会更改,否则您将不断添加它。当已进入旧一代的对象从该缓存过期时,它们将保留在内存中,直到下一个完整的GC。

我使用并发标记扫描收集器的运气要好得多,可以完全消除完整GC中的长暂停。这需要一些调整,并且可能因应用程序而异。以下是我们用来运行 24GB 64 位 JVM 的功能,该 JVM 具有亚秒级 GC 暂停,同时使用大型缓存每秒处理 100 多个页面请求:

-Xms24g -Xmx24g -XX:+UseCompressedOops -XX:NewRatio=4 -XX:SurvivorRatio=8    
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+DisableExplicitGC  
-XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled  
-XX:+CMSScavengeBeforeRemark -XX:CMSInitiatingOccupancyFraction=68

推荐