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