调整JVM参数如下:
-XX:NewSize=10485760—新生代大小为10m
-XX:MaxNewSize=10485760—新生代最大大小为10m
-Xms20M—初始堆大小为20m
-Xmx20M—最大堆大小为20m
-XX:+UseParNewGC:新生代使用ParNewGC垃圾回收器
-XX:+UseConcMarkSweepGC---老年代使用CMS
-XX:+PrintGCDetails---打印GC详细日志
-XX:+PrintGCTimeStamps—打印GC时间
-XX:SurvivorRatio=8—设置eden区和survivor区的比例为8:1:1
-XX:PretenureSizeThreshold=10485760—设置最大对象的阈值为10m
测试demo:
/**
* Survivor区放不下存活对象,部分对象进入老年代
*/
public class demo {
public static void main(String[] args) {
byte[] bytes1 = new byte[1024 * 1024];//1m
bytes1 = new byte[1024 * 1024];//1m
bytes1 = new byte[1024 * 1024];//1m
//直接分配一个5m的对象,由于eden区只有8m,之前已经分配了3m再加上一些未知对象也会占据一定的内存空间,此时必然会引起新生代gc
byte[] bytes3 = new byte[5 * 1024 * 1024];//5m
}
}
GC信息:
以上信息关键关注红框部分,GC过后,此时from区大概被占据85%的内存,老年代中则大致有1026k的空间被占用。
修改下代码,再多分配一个128kb的对象,修改后demo1如下:
/**
* Survivor区放不下存活对象,部分对象进入老年代
*/
public class demo1 {
public static void main(String[] args) {
byte[] bytes1 = new byte[1024 * 1024];//1m
bytes1 = new byte[1024 * 1024];//1m
bytes1 = new byte[1024 * 1024];//1m
byte[] bytes0 = new byte[128 * 1024];//128kb
//直接分配一个5m的对象,由于eden区只有8m,之前已经分配了3m再加上一些未知对象也会占据一定的内存空间,此时必然会引起新生代gc
byte[] bytes3 = new byte[5 * 1024 * 1024];//5m
}
}
GC信息:
先对两个demo的代码进行对比,发现demo1只是比demo多新建了一个128k的对象,所以demo1代码GC后的存活对象会比demo代码GC后的存活对象多128k,接着对比下前后两个demo输出的GC信息,会发现老年代依旧还是被占据着1026k的内存空间,但是from区的内存被占用比例从85%升到了98%,这对比下来就很直观了,128k的对象去了哪里?肯定是from区,因为老年代被占用的内存大小根本没变。
总结
如果Survivor区放不下存活对象,存活对象并不是全都进入老年代,而是部分对象进入老年代,部分对象继续被分配到Survivor区。
PS:还有另一种情况,可参考本篇博文【如果发生oldGC,即使survivor区放的下部分存活对象,对象也会全部进入老年代】。