我有两种类型的对象的大规模应用程序:长生活(缓存)和短生活(请求 - 进程 - 响应)。从理论上讲,对于这种类型的应用程序,我认为可以配置Young和Old空间,因此旧空间消耗是恒定的,因此没有Full GC。
我已经更改了newSize-maxNewSize参数,但是,旧堆继续上升,直到Full GC。每次完整GC后,消耗量降至20%(缓存占20%)。出于某种原因,我的对象进入旧空间。我有两个嫌疑人为什么搬到旧区:
根据这篇文章:http://chaoticjava.com/posts/gc-tips-and-memory-leaks/告诉您是否分配了大对象,这些对象直接转到旧空间。这是真的,如果是,是否有JVM Option参数可以为Young空间设置对象大小阈值?
如果我正确理解了这个过程,那么在移动到旧部分之前,对象会在To-From生存部分之间切换。是否有参数可以设置在移动到旧空间之前要完成To和From之间的切换次数?
还有其他提示吗?
谢谢, 阿玛尔
答案 0 :(得分:5)
听起来你的幸存者空间不够大。你需要使它们足够大,不需要收集任何对象。对象仅在幸存者空间中切换一次。
如果要分配大型对象,可以使用对象池来避免对它们进行GC操作。您是否考虑过将对象池用于请求/进程/响应数据?例如一个简单的方法是使用ThreadLocal。
您是否尝试过G1收集器,该收集器旨在逐步收集您的所有记忆并减少完整GC的重击。
答案 1 :(得分:5)
您确定旧一代的增长不是缓存对象吗?除非您的缓存是固定的并且永远不会改变,否则您将不断添加它。由于已经使旧代的对象从该缓存过期,它们将留在内存中直到下一个完整的GC。
我对并发标记扫描收集器运气好得多,完全消除了完整GC的长暂停。它需要一些调整,它可以根据应用程序而变化。以下是我们用于运行24GB 64位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
答案 2 :(得分:2)
是否有可以设定多少的参数 在To和From之间切换 搬到旧空间之前完成了吗?
是的,-XX:MaxTenuringThreshold
此开关确定在升级到老一代之前,对象在“From”和“To”Survivor空间之间跳转的次数。对于Java 6,最大值为15,对于早期JDK,最大值为31。 The default value is 15 for the parallel collector and is 4 for CMS collectors
来自Sun JVM GC文档,
使用-XX:MaxTenuringThreshold = 0移动 一个年轻人幸存的物体 一代收集立即到 终身一代。
如果你想做相反的事情,如果你没有设置这个值,它将是默认值,这足以决定对象是否需要进入Old - 提供正如@Peter所说,幸存者足够大以容纳这些物体。
你的幸存者比率是什么?什么是你的总堆?
答案 3 :(得分:0)
物体生命时间特征在这里起着至关重要的作用。重要的调节旋钮是幸存者空间的大小,年龄限制和年轻一代的大小。从战略上讲,我们希望物体能够年轻化,所以如果小物品之间存在足够的差距,很多物体将在年轻一代中消亡。此外,我们可以配置临时阈值,以便对象保留在幸存者空间中,以获得所需数量的集合。
由于我们在幸存者空间中保留大量活动对象并将其从一个空间复制到另一个空间以用于许多次要GC,因此会增加次要GC的成本。
保持一个年轻的大一代自然会增加连续的小集合之间的差距,并给予更多的时间来死亡。
通过尝试使用这些变量来减少对象升级到老一代,并考虑可接受的年轻代暂停,可以达到平衡。