JVM参数中有一个比较重要的参数SurvivorRatio,它定义了新生代中Eden区域和Survivor区域(From幸存区或To幸存区)的比例,默认为8,也就是说Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10
-XX:SurvivorRatio
可参考以下计算公式:
Eden = (R*Y)/(R+1+1)
From = Y/(R+1+1)
To = Y/(R+1+1)
其中:
R:SurvivorRatio比例
Y:新生代空间大小
这里举个例子,如果我们通过设置-Xmn60M来指定新生代分配的空间大小,那么Eden则会分配60M * 0.8 = 48M,Survivor一共分配60M * 0.2 = 12M的内存空间
下面我们通过实际命令行的演示来验证上面的结果
-Xmn60M
-XX:SurvivorRatio=8
-XX:+PrintFlagsFinal
uintx NewSize := 62914560 {product}
uintx MaxNewSize := 62914560 {product}
注:这里只列举了部分重要的指标
可以看到新生代内存大小为62914560B=60M已经生效,因为Eden区和Survivor区的大小使用JVM参数无法在控制台中查到,所以我们借助Java VisualVM中的Visual GC插件来查看具体指标
OK,指标验证已完成!
经过上面的测试我们已经大致理解了新生代内存分配的策略,其实到这一步还没有结束,我们还需要搞清楚下面几个问题:
假设1:Eden区域设置太大
新生成的对象会被分配在Eden区,Eden空间不足时会触发MinorGC。理想状态下,如果所有对象在这个阶段全部被回收,Eden区域被清空,不会出什么问题。如果GC后还存在一部分幸存的对象,则会被复制到To Survivor区域,此时因为Survivor区域空间太小无法容纳这些对象,结果大部分幸存对象只在进行一次或很少次的GC后就会被移动到老年代,也就是说从某种程度上来讲失去了MinorGC的初衷,这种情况是肯定不被允许的
假设2:Eden区域设置太小
接着分析,Eden区域设置太小,意味着其空间很快就会被占满,也就是说增加了新生代的GC次数,而频繁的GC会降低整体JVM性能
一般情况下该参数使用默认值即可,除非你在JVM优化领域有着非常丰富的经验。总之,尽可能最小化"短暂对象"移动到老年代的数量,同时最小化年轻代GC的次数和持续时间,要找到适当的折衷方案,首先要了解应用程序中对象年龄的分布情况