11、JVM实战:内存分配策略:长期存活的对象将进入老年代

11–内存分配策略–长期存活的对象将进入老年代


1、测试

1.1、代码

public class Test {
    private static final int _1MB = 1024 * 1024;

  //参数
  //-verbose:gc
  //-Xms20M
  //-Xmx20M
  //-Xmn10M
  //-XX:+PrintGCDetails
  //-XX:SurvivorRatio=8
  //-XX:MaxTenuringThreshold=1   //最大分代年龄为1  
  //-XX:+PrintTenuringDistribution   
  //-XX:+UseSerialGC
    public static void main(String[] args) {
    	
        byte[] allocation1, allocation2, allocation3;

 		//256KB 什么时候进入老年代决定于XX:MaxTenuringThreshold设置
        allocation1 = new byte[_1MB / 4];
       
        allocation2 = new byte[4 * _1MB];//4048KB 
        allocation3 = new byte[4 * _1MB];	//4048KB eden共占用了 8352KB
        allocation3 = null; //断开引用,成为垃圾对象

		 //再申请分配4MB内存,放不下,触发Minor GC
        allocation3 = new byte[4 * _1MB];
    }
}

1.2、日志

 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     896768 bytes,     896768 total
: 6079K->875K(9216K), 0.0036167 secs] 6079K->4971K(19456K), 0.0036538 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        584 bytes,        584 total
: 5056K->0K(9216K), 0.0008881 secs] 9152K->4968K(19456K), 0.0009051 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4316K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000, 0x00000000ff037058, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400248, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4968K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000, 0x00000000ffada120, 0x00000000ffada200, 0x0000000100000000)
 Metaspace       used 3244K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 353K, capacity 388K, committed 512K, reserved 1048576K

3、分析

3.1、第1次GC前

针对输出结果我们可以拆分来看,当allocation1和allocation2对象加载的时候,两个对象加在一起是4.25MB,Eden区都能存放下(Eden区大小9216K),没有任何问题。

我们看到发生GC前,eden使用空间6079K,其中allocation2使用41024K,allocation1使用256K,那么其他系统使用=6079-41024-256=1727K。

对应内存图如下

*

3.2、第1次GC

当allocation3对象创建的时候,这时发现eden区空间不足,则会触发第一次GC

*

3.2.1、日志解读


[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     896768 bytes,     896768 total
: 6079K->875K(9216K), 0.0036167 secs] 6079K->4971K(19456K), 0.0036538 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
GC :

代表发生了一次垃圾回收,前面没有Full修饰,表明这时一次Minor GC;

Allocation Failure:

表明本次引起GC的原因是因为在年轻代中没有足够的空间能够存储新的数据了。

6079K->875K(9216K):

三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。

6079K->4971K(19456K):

三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。

0.0036167 secs

代表本次新生代GC耗时

年轻代回收空间

6079K-875K=5204K=5.082M

堆回收空间

6079K-4971K=1108K

进入老年代空间

5204K-1108K=4096=4M

3.2.2、 内存变化图

*

这里请大家注意:由于我们的 allocation1对象和allocation2对象都是强引用不会被回收,所以肯定会直接放入幸存者区域,allocation1对象可以放入,但是我们的allocation2对象太大是无法放入S1区的,因此根据我们上面讲的垃圾收集器的默认担保机制,allocation2对象会直接进入到我们的老年代进行存放。 这也解释了为什么最终有4096K(4MB)大小的对象进入了老年代

当第一次GC完后Eden区就有足够的空间存放 allocation3对象了。

3.2、第2次GC

allocation3 = null; //这行代码一旦执行,那么我们的allotion3对象没有了直接引用者

*

接着最后一行代码开始执行:

allocation3 = new byte[4 * _1MB]; //再申请分配4MB内存,放不下,触发Minor GC

这次继续申请分配4MB大小对象放入Eden区,那么依然又会存在分配不下触发GC,继续分析如下日志:

[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        584 bytes,        584 total
: 5056K->0K(9216K), 0.0008881 secs] 9152K->4968K(19456K), 0.0009051 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

那么我们本次日志得出的结论:

1、 该次GC新生代减少了5056-0=5056KB;

1、 注意:新生代直接减少到了0!代表所有幸存区S1里的对象全部转移到了老年代!(因为我们的年龄阈值设置的刚好就是1)allocation3对象是直接被回收了;
2、 Heap区总共减少了9152-4968=4184KB;

1、 主要就是我们的allocation3对象以及少量系统对象被回收了;
3、 5056KB-4184KB=872KB代表一共有872KB对象从年轻代转移到了老年代;

最后内存结果如下:

*

跟我们最后的内存日志结果匹配

Heap
 def new generation   total 9216K, used 4316K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000, 0x00000000ff037058, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400248, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4968K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000, 0x00000000ffada120, 0x00000000ffada200, 0x0000000100000000)