深入理解Java虚拟机(六)

文章探讨了Java虚拟机的内存分配和垃圾收集策略,特别是动态对象年龄判定和空间分配担保机制。在MinorGC过程中,当Survivor空间中相同年龄对象大小超过其一半时,这些对象可以直接进入老年代。此外,虚拟机在MinorGC前会检查老年代是否有足够的空间来担保新生代对象的晋升,如果不足,则可能触发FullGC。文章还通过代码示例展示了不同内存分配如何影响垃圾收集行为。
摘要由CSDN通过智能技术生成

垃圾收集器与内存分配策略(五)

内存分配和回收策略(二)

4、动态对象年龄判定

        为了更好的适应不同程序的内存状况,虚拟机并不是强制要求对象年龄必须达到MaxTenuringThreshold才能晋升老年代。如果在Survivor空间中相同年龄对象大小总和大于Survivor空间一半,年龄大于或等于该年龄的对象就可以直接进入老年代。比如有很多的age=n的对象,他们内存之和>Survivor空间一半,则>n的年龄对象直接进入老年代。

/**
 * -XX:+UseSerialGC 使用SerialGC垃圾收集器
 * -verbose:gc 输出虚拟机中GC的详细情况
 * -Xms20M     Java堆最大20M
 * -Xmx20M     Java堆最小20M(说明不可扩展)
 * -Xmn10M     10M分给新生代
 * -XX:+PrintGCDetails
 * -XX:SurvivorRatio=8 新生代Eden区与一个Survivor区的空间比例是8:1
 * -XX:MaxTenuringThreshold=15 当age=15时,放入老年代
 * -XX:+PrintTenuringDistribution
 * -XX:+HandlePromotionFailure
 *
 * @author wcj
 * @date 2019/9/23 16:56
 */
public class AgeObjectGC {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        byte[] b1, b2, b3, b4;
        b1 = new byte[_1MB / 4];//256k  新生代10M,Eden区与Survivor区的大小占比为8M,1M
        b2 = new byte[_1MB / 4];//256k  老年代10M
        b3 = new byte[4 * _1MB];//4MB
        b4 = new byte[4 * _1MB];//4MB
        b4 = null;
        b4 = new byte[4 * _1MB];//4MB

    }
}
输出结果如下:
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     944488 bytes,     944488 total
: 6288K->922K(9216K), 0.0042134 secs] 6288K->5018K(19456K), 0.0042469 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:        280 bytes,        280 total
: 5100K->0K(9216K), 0.0009777 secs] 9196K->5001K(19456K), 0.0009950 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4315K [0x00000006c0000000, 0x00000006c0a00000, 0x00000006c0a00000)
  eden space 8192K,  52% used [0x00000006c0000000, 0x00000006c0436ce0, 0x00000006c0800000)
  from space 1024K,   0% used [0x00000006c0800000, 0x00000006c0800118, 0x00000006c0900000)
  to   space 1024K,   0% used [0x00000006c0900000, 0x00000006c0900000, 0x00000006c0a00000)
 tenured generation   total 10240K, used 5001K [0x00000006c0a00000, 0x00000006c1400000, 0x00000007c0000000)
   the space 10240K,  48% used [0x00000006c0a00000, 0x00000006c0ee2580, 0x00000006c0ee2600, 0x00000006c1400000)
 Metaspace       used 3194K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 354K, capacity 388K, committed 512K, reserved 1048576K

        由结果发现,当设置-XX:MaxTenuringThreshold=15时,新生代中的Survivor空间仍然是0%,因为新生代Eden区与一个Survivor区的空间比例是8:1,所以当b4分配时,剩余的Eden区不够分配4M的内存空间,所以执行到b4时,进行了Minor GC,将b1,b2,b3进行了回收,b3太大(4M)进入不了Survivor(1M),就直接回收进了老年代,而b1,b2进入了Survivor。如下图

byte[] b1, b2, b3, b4;
b1 = new byte[_1MB / 4];//256k  新生代10M,Eden区与Survivor区的大小占比为8M,1M
b2 = new byte[_1MB / 4];//256k  老年代10M
b3 = new byte[4 * _1MB];//4MB
b4 = new byte[4 * _1MB];//4MB
这是输出结果如下:
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     944488 bytes,     944488 total
: 6288K->922K(9216K), 0.0084268 secs] 6288K->5018K(19456K), 0.0084855 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 5182K [0x00000006c0000000, 0x00000006c0a00000, 0x00000006c0a00000)
  eden space 8192K,  52% used [0x00000006c0000000, 0x00000006c04290f0, 0x00000006c0800000)
  from space 1024K,  90% used [0x00000006c0900000, 0x00000006c09e6968, 0x00000006c0a00000)
  to   space 1024K,   0% used [0x00000006c0800000, 0x00000006c0800000, 0x00000006c0900000)
 tenured generation   total 10240K, used 4096K [0x00000006c0a00000, 0x00000006c1400000, 0x00000007c0000000)
   the space 10240K,  40% used [0x00000006c0a00000, 0x00000006c0e00010, 0x00000006c0e00200, 0x00000006c1400000)
 Metaspace       used 3181K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

        这是,可以看出老年代中,使用了4096K,也就是b3进入了老年代,b4进入了Eden区。当执行到最后一行b4=4M时,根据分配原则,优先在Eden分配,但是Eden剩余的空间不足以分配4M的b4,发送了一个Minor GC,因为Survivor区的空间的b1,b2同年龄且占用了Survivor空间的一半,所以直接分配到了老年代,老年代的占用率达到了48%。如果将b4=3M,则不会触发第二次Minor GC,b1,b2还是在新生代的Survivor中。如下图

byte[] b1, b2, b3, b4,b5;
b1 = new byte[_1MB / 4];//256k  新生代10M,Eden区与Survivor区的大小占比为8M,1M
b2 = new byte[_1MB / 4];//256k  老年代10M
b3 = new byte[4 * _1MB];//4MB
b4 = new byte[4 * _1MB];//4MB
b4 = null;
b4 = new byte[3 * _1MB];//3MB
将b4赋值为3M时,应该Eden区,占用4+3M,结果如下:
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     944584 bytes,     944584 total
: 6288K->922K(9216K), 0.0039214 secs] 6288K->5018K(19456K), 0.0039455 secs] [Times: user=0.01 sys=0.01, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 8414K [0x00000006c0000000, 0x00000006c0a00000, 0x00000006c0a00000)
  eden space 8192K,  91% used [0x00000006c0000000, 0x00000006c0750ef0, 0x00000006c0800000)
  from space 1024K,  90% used [0x00000006c0900000, 0x00000006c09e69c8, 0x00000006c0a00000)
  to   space 1024K,   0% used [0x00000006c0800000, 0x00000006c0800000, 0x00000006c0900000)
 tenured generation   total 10240K, used 4096K [0x00000006c0a00000, 0x00000006c1400000, 0x00000007c0000000)
   the space 10240K,  40% used [0x00000006c0a00000, 0x00000006c0e00010, 0x00000006c0e00200, 0x00000006c1400000)
 Metaspace       used 3226K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 355K, capacity 388K, committed 512K, reserved 1048576K

5、空间分配担保

        在发生Minor GC之前,虚拟机会检查老年代最大连续可用空间是否大于新生代所有对象的总空间。如果条件成立,则Minor GC是安全的。反之,虚拟机就会查看HandlePromotionFailure是否允许担保失败。允许:再次检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则进行Minor GC(存在风险);如果小于,或者HandlePromotionFailure参数不允许担保失败,则进行Full GC。

        之前提过新生代采用的是复制收集算法,为了提高内存利用率,只使用了其中一个Survivor空间进行备份,且空间分配较小,当大量对象Minor GC后还存活时,就需要老年代进行分代担保。但前提是,老年代有足够的空间区存储新生代存活的对象,而有多少对象会存活在实际内存回收之前是无法明确知道的,所以就以每次回收晋升老年代对象容量的平均大小作为参考,与老年代实际剩余空间进行比较,决定是否进行Full GC使老年代拥有更多的空间。所以会存在风险,毕竟只是个参考,可成功可失败。

注意:JDK1.6 update24之后,HandlePromotionFailure参数不会影响到虚拟机的空间分配担保策略。规则变为只要老年代的连续空间大于新生代总大小或历次晋升的平均大小就会进行Minor GC,否则进行Full GC。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芦蒿炒香干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值