代码清单3-7 长期存活的对象将进入老年代《深入理解Java虚拟机》周志明

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时,就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这点,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能别Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

读者可以试试分别以-XX:MaxTenuringThreshold=1和-XX:MaxTenuringThreshold=15两种设置来执行代码清单3-7中的testTenuringThreshold()方法,此方法中的allocation1对象需要256KB内存,Survivor空间可以容纳。当MaxTenuringThreshold=1时,allocation1对象在第二次GC发生时进入老年代,新生代已使用的内存GC后非常干净地变成0KB。而MaxTenuringThreshold=15时,第二次GC发生后,allocation1对象则还留在新生代Survivor空间,这时新生代仍然有404KB被占用。

代码清单3-7 长期存活的对象进入老年代

package lime.jvm._003._006._003;

/**
 * @Author : Liangmy
 * @Description :
 * @Date : Created in 2020/1/5 下午12:56
 * @Modified By :
 * <p>
 * 代码清代3-7 长期存活的对象进入老年代
 * <p>
 * VM Args : -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * <p>
 */
public class TestTenuringThreshold {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        testTenuringThreshold();
    }

    private static void testTenuringThreshold() {
        byte[] allocation1, allocation2, allocation3;
        allocation1 = new byte[_1MB / 4];
        // 什么时候进入老年代取决于XX:MaxTenuringThreshold设置
        allocation2 = new byte[4 * _1MB];
        allocation3 = new byte[4 * _1MB];
        allocation3 = null;
        allocation3 = new byte[4 * _1MB];
    }
}

以MaxTenuringThreshold=1参数来运行的结果:

localhost:_003 liangmy$ java -version

java version "1.8.0_151"

Java(TM) SE Runtime Environment (build 1.8.0_151-b12)

Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

localhost:_003 liangmy$ java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold

[GC (Allocation Failure) [DefNew

Desired survivor size 524288 bytes, new threshold 1 (max 1)

- age   1:     547200 bytes,     547200 total

: 5023K->534K(9216K), 0.0049281 secs] 5023K->4630K(19456K), 0.0049702 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] 

[GC (Allocation Failure) [DefNew

Desired survivor size 524288 bytes, new threshold 1 (max 1)

: 4630K->0K(9216K), 0.0015036 secs] 8726K->4628K(19456K), 0.0015442 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

Heap

 def new generation   total 9216K, used 4178K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)

  eden space 8192K,  51% used [0x00000007bec00000, 0x00000007bf014930, 0x00000007bf400000)

  from space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)

  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)

 tenured generation   total 10240K, used 4628K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)

   the space 10240K,  45% used [0x00000007bf600000, 0x00000007bfa85280, 0x00000007bfa85400, 0x00000007c0000000)

 Metaspace       used 2646K, capacity 4486K, committed 4864K, reserved 1056768K

  class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

以MaxTenuringThreshold=15参数来运行的结果:

以MaxTenuringThreshold=15参数来运行的结果(测试结果不一致):

localhost:_003 liangmy$ java -version

java version "1.8.0_151"

Java(TM) SE Runtime Environment (build 1.8.0_151-b12)

Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

localhost:_003 liangmy$ java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold

[GC (Allocation Failure) [DefNew

Desired survivor size 524288 bytes, new threshold 1 (max 15)

- age   1:     547200 bytes,     547200 total

: 5023K->534K(9216K), 0.0058685 secs] 5023K->4630K(19456K), 0.0059285 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

[GC (Allocation Failure) [DefNew

Desired survivor size 524288 bytes, new threshold 15 (max 15)

: 4630K->0K(9216K), 0.0015947 secs] 8726K->4628K(19456K), 0.0016348 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 

Heap

 def new generation   total 9216K, used 4178K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)

  eden space 8192K,  51% used [0x00000007bec00000, 0x00000007bf014930, 0x00000007bf400000)

  from space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)

  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)

 tenured generation   total 10240K, used 4628K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)

   the space 10240K,  45% used [0x00000007bf600000, 0x00000007bfa85280, 0x00000007bfa85400, 0x00000007c0000000)

 Metaspace       used 2646K, capacity 4486K, committed 4864K, reserved 1056768K

  class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

第二次GC时候仍然是GC掉了。

愁人。。。 

 

引用1:读《深入理解jvm虚拟机》之长期存活对象进入老年代,有感!!!!

引用2:java虚拟机长期存活的对象将进入老年代实验结果与书上不同?

引用3:MaxTenuringThreshold 和 TargetSurvivorRatio参数说明

总结:

Desired survivor size 524288 bytes

关于这个512KB空间是怎么来的,JVM有这样一个参数:-XX:TargetSurvivorRatio:目标存活率,默认为50%,表明所有age的survivor space对象的大小如果超过Desired survivor size,则重新计算threshold,以age和MaxTenuringThreshold的最小值为准,否则以MaxTenuringThreshold为准。

-XX:TargetSurvivorRatio
  设定survivor区的目标使用率。默认50,即survivor区对象目标使用率为50%。

计算公式: (survivor_capacity * TargetSurvivorRatio) / 100 * sizeof(a pointer):survivor_capacity(一个survivor space的大小)乘以TargetSurvivorRatio,表明所有age的survivor space对象的大小如果超过Desired survivor size,则重新计算threshold,以age和MaxTenuringThreshold的最小值为准,否则以MaxTenuringThreshold为准。

JVM会将每个对象的年龄信息、各个年龄段对象的总大小记录在“age table”表中。基于“age table”、survivor区大小、survivor区目标使用率(-XX:TargetSurvivorRatio)、晋升年龄阈值(-XX:MaxTenuringThreshold),JVM会动态的计算tenuring threshold的值。一旦对象年龄达到了tenuring threshold就会晋升到老年代。

为什么要动态的计算tenuring threshold的值呢?假设有很多年龄还未达到TenuringThreshold的对象依旧停留在survivor区,这样不利于新对象从eden晋升到survivor。因此设置survivor区的目标使用率,当使用率达到时重新调整TenuringThreshold值,让对象尽早的去old区。

Survivor空间中相同年龄所有对象大小的总和(meta-class大小)大于Survivor空间的一半导致的提前进入老年区。主要是因为class-path也占用了大小。

提升Survivor空间或者减小allocation1的空间之后就跟书上一致了。

1* 提升Survivor空间

package lime.jvm._003._006._003;

/**
 * @Author : Liangmy
 * @Description :
 * @Date : Created in 2020/1/5 下午12:56
 * @Modified By :
 * <p>
 * 代码清代3-7 长期存活的对象进入老年代
 * <p>
 * VM Args : -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * 1* 提升Survivor空间 : java -Xms80M -Xmx80M -Xmn40M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 *
 * <p>
 */
public class TestTenuringThreshold {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        testTenuringThreshold();
    }

    private static void testTenuringThreshold() {
        byte[] allocation1, allocation2, allocation3;
        allocation1 = new byte[_1MB]; // (eden(32MB) allocation1 = 1MB, from survivor(4MB) 0MB, to survivor(4MB) 0MB, tenure gen(40MB) 0MB)
        // 什么时候进入老年代取决于XX:MaxTenuringThreshold设置
        allocation2 = new byte[16 * _1MB]; // (eden(32MB) allocation1 = 1MB, allocation2 = 16MB,from survivor(4MB) 0MB, to survivor(4MB) 0MB, tenure gen(40MB) 0MB)
        // 1MB + 16MB + 16MB = 33MB > eden(32MB) 触发Minor GC
        // Desired survivor size = (from survivor(4MB) * TargetSurvivorRatio) / 100 * sizeof(a pointer) = (4MB * 50) / 100 * sizeof(a ppointer)
        // Desired survivor size > 1MB
        // Desired survivor size < 16MB
        // GC 后 (eden(32MB) 0MB, from survivor(4MB) 1MB, to survivor(4MB) 0MB, tenure gen(40MB) 16MB)
        allocation3 = new byte[16 * _1MB]; // (eden(32MB) allocation3 = 16MB, from survivor(4MB) 1MB, to survivor(4MB) 0MB, tenure gen(40MB) 16MB)
        allocation3 = null;
        // 16MB + 16MB + other > eden(32MB) 触发Minor GC
        // 16MB 没有引用 GC
        // Desired survivor size < 16MB
        // from survivor(4MB) 1MB age = 1 < 15
        // GC 后 (eden(32MB) allocation3 = 16MB, from survivor(4MB) 1MB, to survivor(4MB) 0MB, tenure gen(40MB) 16MB)
        allocation3 = new byte[16 * _1MB]; // (eden(32MB) allocation3 = 16MB, from survivor(4MB) 1MB, to survivor(4MB) 0MB, tenure gen(40MB) 16MB)
    }
}

GC log : 

localhost:_003 liangmy$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
localhost:_003 liangmy$ java -Xms80M -Xmx80M -Xmn40M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
[GC (Allocation Failure) [DefNew
Desired survivor size 2097152 bytes, new threshold 15 (max 15)
- age   1:    1333632 bytes,    1333632 total
: 18718K->1302K(36864K), 0.0176838 secs] 18718K->17686K(77824K), 0.0177439 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 2097152 bytes, new threshold 15 (max 15)
- age   2:    1331824 bytes,    1331824 total
: 17686K->1300K(36864K), 0.0015781 secs] 34070K->17684K(77824K), 0.0015996 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 36864K, used 18668K [0x00000007bb000000, 0x00000007bd800000, 0x00000007bd800000)
  eden space 32768K,  53% used [0x00000007bb000000, 0x00000007bc0f5dc8, 0x00000007bd000000)
  from space 4096K,  31% used [0x00000007bd000000, 0x00000007bd145270, 0x00000007bd400000)
  to   space 4096K,   0% used [0x00000007bd400000, 0x00000007bd400000, 0x00000007bd800000)
 tenured generation   total 40960K, used 16384K [0x00000007bd800000, 0x00000007c0000000, 0x00000007c0000000)
   the space 40960K,  40% used [0x00000007bd800000, 0x00000007be800010, 0x00000007be800200, 0x00000007c0000000)
 Metaspace       used 2646K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

2* 减小allocation1的空间

package lime.jvm._003._006._003;

/**
 * @Author : Liangmy
 * @Description :
 * @Date : Created in 2020/1/5 下午12:56
 * @Modified By :
 * <p>
 * 代码清代3-7 长期存活的对象进入老年代
 * <p>
 * VM Args : -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * 1* 提升Survivor空间 : java -Xms80M -Xmx80M -Xmn40M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 * 1* 减小allocation1的空间 : java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
 *
 * <p>
 */
public class TestTenuringThreshold {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        testTenuringThreshold();
    }

    private static void testTenuringThreshold() {
        byte[] allocation1, allocation2, allocation3;
        allocation1 = new byte[_1MB / 8]; // (eden(8MB) allocation1 = 0.125MB, from survivor(1MB) 0MB, to survivor(1MB) 0MB, tenure gen(10MB) 0MB)
        // 什么时候进入老年代取决于XX:MaxTenuringThreshold设置
        allocation2 = new byte[4 * _1MB]; // (eden(8MB) allocation1 = 0.125MB, allocation2 = 4MB,from survivor(1MB) 0MB, to survivor(1MB) 0MB, tenure gen(10MB) 0MB)
        // 0.125MB + 4MB + 4MB = 8.125MB > eden(8MB) 触发Minor GC
        // Desired survivor size = (from survivor(1MB) * TargetSurvivorRatio) / 100 * sizeof(a pointer) = (1MB * 50) / 100 * sizeof(a ppointer)
        // Desired survivor size > 0.125MB
        // Desired survivor size < 4MB
        // GC 后 (eden(8MB) 0MB, from survivor(1MB) 0.125MB, to survivor(1MB) 0MB, tenure gen(10MB) 4MB)
        allocation3 = new byte[4 * _1MB]; // (eden(8MB) allocation3 = 4MB, from survivor(1MB) 0.125MB, to survivor(1MB) 0MB, tenure gen(10MB) 4MB)
        allocation3 = null;
        // 4MB + 4MB + other > eden(8MB) 触发Minor GC
        // 4MB 没有引用 GC
        // Desired survivor size < 4MB
        // from survivor(1MB) 0.125MB age = 1 < 15
        // GC 后 (eden(8MB) allocation3 = 4MB, from survivor(1MB) 0.125MB, to survivor(1MB) 0MB, tenure gen(10MB) 4MB)
        allocation3 = new byte[4 * _1MB]; // (eden(8MB) allocation3 = 4MB, from survivor(1MB) 0.125MB, to survivor(1MB) 0MB, tenure gen(10MB) 4MB)
    }
}

GC log : 

localhost:_003 liangmy$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
localhost:_003 liangmy$ java -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -verbose:gc -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+UseSerialGC lime.jvm._003._006._003.TestTenuringThreshold
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:     416128 bytes,     416128 total
: 4895K->406K(9216K), 0.0067625 secs] 4895K->4502K(19456K), 0.0068236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   2:     414320 bytes,     414320 total
: 4502K->404K(9216K), 0.0011564 secs] 8598K->4500K(19456K), 0.0011933 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4746K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  eden space 8192K,  53% used [0x00000007bec00000, 0x00000007bf03d8a0, 0x00000007bf400000)
  from space 1024K,  39% used [0x00000007bf400000, 0x00000007bf465270, 0x00000007bf500000)
  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
 tenured generation   total 10240K, used 4096K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
   the space 10240K,  40% used [0x00000007bf600000, 0x00000007bfa00010, 0x00000007bfa00200, 0x00000007c0000000)
 Metaspace       used 2646K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

3.6.4 动态对象年龄判定 :如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值