JVM垃圾回收算法

JVM垃圾回收算法


一、复制算法

上一篇文章说到,jvm通过可达性算法,标记堆中的实例对象是否直接或者间接的被GC Roots引用,而没有引用的则是垃圾对象,等待回收的,那么什么时候会回收这些对象呢?新生代存储满了,存不了下一个新生成的实例对象,那么就是触发一次youngGc,回收掉垃圾对象。

1、触发youngGc条件以及回收机制

堆内存新生代中分为三个区域,eden区、survivor1区、survivor2区,默认内存大小比例是 8:1:1。

新对象进入的区域是eden区,而survivor区其中一个存储的是上一次youngGc存活下来的对象,当触发一次youngGc时,会对eden区和有对象 的survivor进行标记,并且回收垃圾对象,然后存活下来的对象,全部转移到另外一个没有存储的survivor区,而下一次youngGc则又是标记回收,eden区和现在存储上一次存活对象的survivor区。

jvm为什么需要用这么复杂的方式来回收对象呢?

垃圾回收的性能和内存碎片的控制

举一个例子,如果直接对堆的新生代区域进行回收,存活的对象之间相邻有很多内存碎片,新生的对象大小是不确定,不可能将大小不一内存碎片分配给新生对象,只能开辟新的内存空间,而这些内存碎片,会随着youngGC次数而增多,对内存导致了极大的浪费。

那么如果将新生代一分为二,新生对象到其中一片空间,每一次youngGC将存活的对象移动到另一片内存空间整齐排列,来回反复,这样就没有了内存碎片的困扰,但是这样内存实际上只有一般得到了利用,而我们知道其实,新生代中的对象大多数都是垃圾对象,只有少数对象会在一次youngGC存活下来,大概1%左右,所以就有了现在复制算法的优化。

现在的其中一个survivor还是空闲,但是可以接受,只是对大概10%的内存闲置。

JVM垃圾回收算法

2、对象是如何进入老年代的
  • 过15次GC之后进入老年代

    可以通过JVM参数“-XX:MaxTenuringThreshold”来设置,默认是15岁

  • 动态年龄判断

    当前放对象的Survivor区域里,一批对象的总大小大于了这块Survivor区域的内存大小的50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了

    举一个例子:年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升。

    这样的做的原因还是想,提前将长期存活的对象进入老年代

  • 大对象直接进入老年代

    有一个JVM参数,就是“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如“1048576”字节,就是1MB。

    类似一个超大数组,一直来回在年轻代复制,很耗费时间,所以直接让他进入老年代。

  • Minor GC后的对象太多无法放入Survivor区

    假设在发生GC的时候,发现Eden区里超过150MB的存活对象(假设Survivor区是100M),此时没办法放入Survivor区中,此时该怎么办呢?
    这个时候就必须得把这些对象直接转移到老年代去。

  • 老年代空间分配担保规则

    如果新生代里有大量对象存活下来,确实是自己的Survivor区放不下了,必须转移到老年代去 那么如果老年代里空间也不够放这些对象呢?这该咋整呢?

    判断一 JVM会先检查一下老年代可用的可用内存空间,是否大于新生代所有对象的总大小。 为啥要检查这个

    因为在极端的情况可能存在一次young GC后,所有新生代对象全部存活,都进入老年代。

    所以如果新生代所有对象的总大小小于老年代可用内存,则可以放心的young GC,否则的话就会看是否配置了”-XX:-HandlePromotionFailure“,有则走下一个判断:

    看看老年代的内存是否大于之前每一次young GC后进入老年代的平均大小。大于的话,也执行young Gc。

    冒险执行young Gc之后

    • 第一种可能,young Gc过后,剩余的存活对象的大小,是小于Survivor区的大小的,那么此时存活对象进入Survivor区域即可。

    • 第二种可能,Minor GC过后,剩余的存活对象的大小,是大于 Survivor区域的大小,但是是小于老年代可用内存大小的,此时就直接进入老年代即可。

    • 第三种可能,很不幸,Minor GC过后,剩余的存活对象的大小,大于了Survivor区域的大小,也大于了老年代可用内存的大小。此时老年代都放不下这些存活对象了,就会发生“Handle Promotion Failure”的情况,这个时候就会触发一次“Full GC”。Full GC就是对老年代进行垃圾回收,同时也一般会对新生代进行垃圾回收。因为这个时候必须得把老年代里的没人引用的对象给回收掉,然后才可能让Minor GC过后剩余的存活对象进入老年代里面。

    如果要是Full GC过后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么此时就会导致所谓的“OOM”内存溢出了

新生代进入老年代规则

3、老年代垃圾回收算法

老年代采取的是**标记清理算法** ,通过GC Roots标记垃圾对象和存活对象,标记完成后,不是清理掉需要回收的对象,而是将所有存活的对象向一端移动,然后将边界以外的内存全部清理掉,这样可以有效避免空间碎片的产生。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值