JVM GC(jdk1.7)
一、minor gc
发生在Eden、From、To之间,垃圾对象清理,存活对象从【Eden、From】复制到To,或者【Eden、To】复制到From
晋升老年代
在Survivor存活的对象,一般情况下(注意这里用词),年龄达到一定阈值,将晋升到老年代,阈值通过-XX:MaxTenuringThreshold设置,默认15,例如-XX:MaxTenuringThreshold=2
大对象
-XX:PretenureSizeThreshold 大于这个设置值的对象可以直接在老年代分配对象
这样做的好处是:避免在Eden区及两个Survivor区之前发生大量的内存复制
动态对象年龄判定
前面说关于GC年龄时用词是一般情况下,这里讲讲特殊情况
并不一定要等到-XX:MaxTenuringThreshold,对象才进入老年代,在Survivor区相同的对象且年龄相同,总和大于Survivor的一半,也可以直接进入老年代
下例:
Dog1和Dog2年龄一样,且Dog1和Dog2年龄大于Survivor的一半,将直接进入老年代
GC 日志
-
例1
survivor不够复制,直接进入老年代
package com.test.allocation;
/**
* @author mdl
* @date 2020/5/13 15:40
*/
public class TestAllocation {
private static final int _1MB = 1024 * 1024;
/**
* VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
* Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M)
*/
public static void testAllocation(){
byte[] allocation1;
byte[] allocation2;
byte[] allocation3;
byte[] allocation4;
allocation1 =new byte[2* _1MB];
allocation2 =new byte[2* _1MB];
allocation3 =new byte[2* _1MB];
// 此时Eden已分配了6M,剩余2M不足以分配allocation4,触发Minor GC
// GC期间,试图将存活的3个2M的对象复制到Survivor区,但是Survivor空间只有1M
// 这时通过分配担保机制提前转义到老年代
allocation4 =new byte[4* _1MB];
// 所以此次GC结束:4M的allocation4进入Eden区,Survivor区空闲,老年代被占用6M
}
public static void main(String[] args) {
TestAllocation.testAllocation();
}
}
-
分配allocation4时,发现Eden已经分配了6M(Eden有8M空间),不足以分配allocation4,发生minor gc
-
gc期间,针对Eden,3个2M对象(allocation1、2、3)无法在Survivor分配,这时通过分配担保进入老年代
,老年代占用空间为6M
-
新生代资源利用为eden+from = 8192K + 1024K= 9216K,即9M,腾出了4M给allocation4利用
-
例2
大对象直接进入老年代
package com.test.allocation; /** * 大对象直接进入老年代 * * @author mdl * @date 2020/5/13 15:40 */ public class TestPretenuerSizeThreshold { private static final int _1MB = 1024 * 1024; /** * VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728 * Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M) */ public static void testAllocation(){ byte[] allocation =new byte[4* _1MB]; } public static void main(String[] args) { TestPretenuerSizeThreshold.testAllocation(); } }
从GC日志上看出,老年代的占用达到40%,总共10M,使用了4M,可以知道4M的allocation对象直接进入了老年代,这是因为PretenureSizeThreshold参数被设置为3MB
-
例3
相同的对象且年龄相同,空间利用率大于Survivor(From或To)的一半,gc时进入老年代(这其实是个误区)
package com.test.allocation; /** * 动态对象年龄判定 * * @author mdl * @date 2020/5/13 15:40 */ public class TestTenuringThreshold { private static final int _1MB = 1024 * 1024; /** * VM 参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution * Java堆大小20M,不可扩展,年轻代10M,年老代10M(Eden:8M, From:1M, To:1M) */ public static void testAllocation() { byte[] allocation1 = new byte[_1MB / 4]; // allocation1+ allocation2 大于survivor空间的一半了 byte[] allocation2 = new byte[_1MB / 4]; byte[] allocation3 = new byte[4 * _1MB]; // 第一次Minor GC allocation1和allocation2年龄为1 byte[] allocation4 = new byte[4 * _1MB]; // 将allocation4置空,在下一次回收将被回收 allocation4 = null; // 第二次Minor GC allocation1和allocation2进入老年代 allocation4 = new byte[4 * _1MB]; // 1 } public static void main(String[] args) { TestTenuringThreshold.testAllocation(); } }
如果注释掉代码1处
分析:
只发生一次minor gc,allocation4分配前,eden已占用1/2 M+ 4M=4.5M,还剩1.5M,不足以分配allocation4
allocation3进入老年代,allocation1、2进入from,eden分配allocation4
至于为什么结果from是100%,暂时未可知
释放代码1处
survivor空闲,而老年代空间增加了10%,allocation1、2进入了老年代了
当然以上推断【对象相同且年龄相同,空间大约survivor一半】其实是有问题的
我们假如:
-
MaxTenuringThreshold为15
-
年龄1对象30%
-
年龄2对象30%
-
年龄3对象40%
可以看到survivor已经100%了,但是对象就不晋升,导致老年代有空间,对象就停留在年轻代,这明显与jvm的表现不符。
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) { //survivor_capacity是survivor空间的大小,TargetSurvivorRatio:目标存活率,默认50 size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100); size_t total = 0; uint age = 1; while (age < table_size) { total += sizes[age];//sizes数组是每个年龄段对象大小 if (total > desired_survivor_size) break; age++; } uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold; ... }
代码的意思大概总结为:
从age为1开始,累加每一个年龄段对象大小,如果大小超过survivor空间一半,取age与MaxTenuringThreshold的最小值暂记为result,大于等于result的age将进入老年代
年龄1(30%)+年龄2(30%)> 50%,那么大于等于年龄2的将晋升到老年代
参考:
https://www.cnblogs.com/lovellll/p/10246073.html
《深入理解Java虚拟机》
-