1、前文回顾
略..
2、动态年龄判定规则
进入老年代的4个时机:1、年龄到了 2、动态年龄判断 3、Survivor 放不下 4、大对象(G1例外)
动态年龄判断:Young GC时,新对象(1岁)进入S区,如果Survivor区域内“年龄1+年龄2+...+年龄n”的对象总和大于Survivor区的50%,此时年龄n以上的对象会进入老年代。
新生代分配了10M,Eden 8M,From 1M ,To 1M
3、动态年龄判定规则的部分示例代码
-XX:NewSize=10485760
-XX:MaxNewSize=10485760
-XX:InitialHeapSize=20971520
-XX:MaxHeapSize=20971520
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=15
-XX:PretenureSizeThreshold=10485760
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:d:/gc.log
public class _44ToOldGC {
static List<byte[]> longActiveList = new ArrayList<>();//长期存活
static final int M = 1024 * 1024;
static final int K = 1024;
public static void main(String[] args) throws InterruptedException {
System.gc();//先清除堆中的垃圾对象,否则后面的数值大小令人费解。不过运行过程中还是有额外的小空间多出来
/**
* 堆 20M
* 新生代 10M (Eden 8M,From 1M ,To 1M)
* 老年代 10M
*/
allocate(2*M);
allocate(2*M);
allocate(2*M);
addLongActiveObj(128*K);
allocate(2*M);//此处触发YoungGC
}
private static void allocate(int size) {
byte[] bytes = new byte[size];
}
private static void addLongActiveObj(int size){
longActiveList.add(new byte[size]);
}
}
4、部分示例代码运行后产生的gc日志
0.133: [Full GC (System.gc()) 0.133: [CMS: 0K->726K(10240K), 0.0033735 secs] 2230K->726K(19456K), [Metaspace: 3361K->3361K(1056768K)], 0.0034877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.138: [GC (Allocation Failure) 0.138: [ParNew: 6473K->192K(9216K), 0.0006125 secs] 7200K->918K(19456K), 0.0006481 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 2404K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 27% used [0x00000000fec00000, 0x00000000fee29280, 0x00000000ff400000)
from space 1024K, 18% used [0x00000000ff500000, 0x00000000ff530010, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
concurrent mark-sweep generation total 10240K, used 726K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3438K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 374K, capacity 388K, committed 512K, reserved 1048576K
解读:
[CMS: 0K->726K(10240K), 0.0033735 secs] 2230K->726K(19456K)
0K->726K :有726K 晋升到老年代
2230K->726K(19456K):堆中2230K(老年代0,新生代2230),堆中回收后就剩晋升到老年代的726K
ParNew: 6473K->192K(9216K):6473(多个byte数组+未知对象),192K(128K的byte数组+未知存活对象)
allocate(2*M);
allocate(2*M);
allocate(2*M);
addLongActiveObj(128*K);
allocate(2*M);//此处触发YoungGC
第五行代码触发了Young GC, 前三行代码分配的对象和其他一些未知对象被回收 ,回收后剩余192K
5、部分代码的GC日志分析
见 4
6、动态年龄判断
将addLongActiveObj(128*K);改为addLongActiveObj(513*K); 使长期存活的占了From区的一半多一点点
然后在加入四行
allocate(2*M);
allocate(2*M);
allocate(2*M);
allocate(2*M);
0.140: [GC (Allocation Failure) 0.140: [ParNew: 6984K->577K(9216K), 0.0005920 secs] 7703K->1295K(19456K), 0.0006294 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.141: [GC (Allocation Failure) 0.141: [ParNew: 6939K->4K(9216K), 0.0006386 secs] 7658K->1257K(19456K), 0.0006760 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
执行第四行时,触发YoungGC, 这次Young GC 没有Eden区的对象存活,此时Survivor区的存活对象年龄+1 变为2岁,一批同龄对象,直接超过了Survivor区空间的50%,此时也可能会导致对象进入老年代。
??按照文中的动态年龄判断逻辑不是应该“大于2岁”的对象进入老年代么? 如果按照专栏25讲描述的动态年龄判断逻辑倒是可以解释的通(一批同龄对象,直接超过了Survivor区空间的50%,此时也可能会导致对象进入老年代)
答:对的,他就是同龄对象超过了Survivor的50%,所以进入老年代了
PS:
1、如果直接allocate(8*M),真正的大小比8M多一点点,Eden区肯定是放不下的,虽然没满足设置的大对象10M的要求,连YoungGC都不触发,直接进入老年代
2、如果直接allocate(7*M+1000*K); 会回收两次,第一次放入时判断Eden放不下,触发YoungGC,回收之前allocate(2*M);放入的对象,放入后,可能系统又放了对象进来,有触发了一次 allocate(7*M+1000*K)被回收了
7、分析最终版的GC日志
见6
8、今日思考题
代码模拟出来对象达到5岁年龄之后自然进入老年代的场景
public static void maxTenuringTest() {
for(int i=0;i<10;i++){
allocate(4 * M);
longActiveList.add(new byte[32*K]);
}
}
GC日志:
0.137: [GC (Allocation Failure) 0.137: [ParNew: 4423K->80K(9216K), 0.0006453 secs] 5141K->797K(19456K), 0.0006869 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
0.138: [GC (Allocation Failure) 0.138: [ParNew: 4373K->78K(9216K), 0.0003029 secs] 5091K->795K(19456K), 0.0003315 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.139: [GC (Allocation Failure) 0.139: [ParNew: 4371K->108K(9216K), 0.0004725 secs] 5089K->825K(19456K), 0.0005057 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.140: [GC (Allocation Failure) 0.140: [ParNew: 4398K->140K(9216K), 0.0002472 secs] 5116K->858K(19456K), 0.0002849 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.140: [GC (Allocation Failure) 0.140: [ParNew: 4429K->178K(9216K), 0.0003791 secs] 5147K->896K(19456K), 0.0004119 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.141: [GC (Allocation Failure) 0.141: [ParNew: 4466K->176K(9216K), 0.0004767 secs] 5184K->944K(19456K), 0.0005208 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.142: [GC (Allocation Failure) 0.142: [ParNew: 4463K->174K(9216K), 0.0003166 secs] 5232K->975K(19456K), 0.0003466 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.143: [GC (Allocation Failure) 0.143: [ParNew: 4460K->168K(9216K), 0.0004341 secs] 5262K->1002K(19456K), 0.0004739 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.144: [GC (Allocation Failure) 0.144: [ParNew: 4454K->172K(9216K), 0.0003660 secs] 5288K->1038K(19456K), 0.0004041 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
maxTenuringTest方法,循环第二次 allocate(4 * M);开始,会不断触发YoungGC,第六次开始,Survivor 区维持在174K左右,但堆的剩余在不断的增长,如下图。主要是因为每次有一个新的进入S区,就有一个年龄大的晋升到老年代。
本文是《从 0 开始带你成为JVM实战高手》内容总结,版权问题,特此声明。详细内容:
如果购买,成功后加QQ群找群主返现10元