搜索JVM内存新生代的比例,几乎所有的文章都是s0:s1:eden = 1:1:8,但是如果你真的手动去测试过,就会发现事实并非如此。

本文会使用到JvisualVM和一些jvm参数命令,可以参考JVM性能监控与调优进行阅读。

准备工作

为了验证该问题,首先来一段测试代码,向list集合中添加对象,模拟内存溢出。

import java.util.ArrayList;
import java.util.List;
public class Testa {
public static void main(String[] args) throws InterruptedException {
List list = new ArrayList<>();
while(true) {
list.add(new Object());
Thread.sleep(100); // 避免内存过快溢出
}
}
}

然后启动代码,通过 JvisualVM 可以清晰的看到新生代内存比例Survivor0 =Survivor1=435.5M,Eden = 1.328G,比例明显不等于1:1:8

关闭内存分配策略自适应

查询资料后得知,JVM有一个关于survivor和eden区大小自动变化的一个参数设置-XX:-UseAdaptiveSizePolicy,是默认开启的,关掉就是严格的1:8了。

于是使用相关命令查看jvm参数,果不其然,该参数是开启的。

C:\Users\mao>jps
1412 Testa
1588 RemoteMavenServer
5732
8100 Launcher
11800 Jps
6044 Main
// 该命令作用是输出18452进程的UseAdaptiveSizePolicy参数
// +UseAdaptiveSizePolicy中的+表示已启用
C:\Users\mao>jinfo -flag UseAdaptiveSizePolicy 18452
-XX:+UseAdaptiveSizePolicy

接下来在代码的启动参数中加上-XX:-UseAdaptiveSizePolicy,表示关闭内存分配策略自适应,然后启动代码。IDEA在Run - Edit Configurations - VM options中添加启动参数。

然后惊奇的Eden/Survivor=1020M÷170M=6,所以s0:s1:eden仍不是1:1:8,而是s0:s1:eden=1:1:6。

显式设置内存比例

又从《深入理解JVM》p92中得知,可以使用-XX:SurvivorRatio=8设置新生代中一个Survivor区与Eden区的的比例是1:8,于是使用jinfo查看jvm参数,得知默认值即为8。

C:\Users\mao>jinfo -flag SurvivorRatio 17588
-XX:SurvivorRatio=8

至此,我已大概确信s0:s1:eden=1:1:6了,但是我又做了一次尝试,在代码的启动jvm参数中添加了-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8。然后查看新生代内存比例如下图,Eden/Survivor=1.063*1024M÷170M=8,终于得到了s0:s1:eden=1:1:8的结果。

总结

JVM内存的新生代比例s0:s1:eden=1:1:8有两个前提条件:

关闭内存分配策略自适应(默认开启),-XX:-UseAdaptiveSizePolicy

手动设置Eden区与Survivor区比例,-XX:SurvivorRatio=8

问题

为什么默认设置的-XX:SurvivorRatio=8并不生效,需要手动开启?

我查看了OpenJDK的源码,并没有找到原因,希望有大神不吝赐教。

JVM内存的新生代比例问题,困惑了我一年之久,期间问过几次大神,都没有得到答案,感谢图灵学院-诸葛老师的帮助,大致解决了该问题,希望后面的时间里,能解决剩下的这个小问题。