JVM垃圾回收算法以及垃圾收集器种类

对象是否可回收判断方式:
1.引用计数法:
对象每被引用一次,其引用计数就+1,不再引用时就-1,这样虽然简单高效,但是无法解决互相引用的问题,比如A持有B,B持有A A a=new A(); B b =new B(); a.instance=b; b.instance=a; a=null;b=null; 这样引用计数都不为0,也就无法回收
2.可达性分析:
确定一批GC Roots引用,然后以这些引用为出发点一级级的查找可以引用到的对象,但凡不能被引用到的对象,就属于废对象,搜索经过的路径称为引用链
垃圾回收算法:
1.标记-清除法:
通过可达性分析标记出需要清除的对象,然后进行清除。标记和清除两步效率都不高,而且空间利用率较低,清除法会产生大量的空间碎片,以致于后来就算剩余空间很大却无法分配较大对象内存的情况。下面的算法都是在此基础上进行了优化
2.复制法:
分配两块一样大小的内存空间,当其中一块内存不足时,将其中的有效对象标记出来复制到另一块空间中。
现在的商业虚拟机都采用这种方法进行回收新生代,但并不是将新生代分为两个一样大小的内存,由于考虑到98%的对象都是很快死亡的,所以将新生代分为 一个大的eden区和两个小的survivor区,每次只使用eden区和其中一个survivor区,回收时将这两个区中的存活对象复制到另外一个s区。默认情况eden区和s区的比例是8:1,这样只会浪费10%的空间而不是50%,主要是根据其普遍的存活率来设计的。考虑到有些情况下存活对象内存>s区大小,需要加入内存担保,保证在S区不够的情况下,还有内存可以容纳多出的这部分对象。
3.标记-移动法:
移动法是将有效的移向一边,无效的移向另一边,然后将无效的全部清除,这种方式在分配对象时很简单
4.分代回收算法:
这个算法并没有什么新鲜的东西,只是根据java对象的生存规律(大部分对象都是朝生夕死)将堆分为新生代和老年代,对象都在新生代中创建,大部分也会在新生代中被回收,而留下的大年龄对象都被转移到老年代中,对于新生代,由于存活率很低,所以适合采用复制法,而老年代对象存活率高,采用标记清除法或者标记移动法进行回收。

垃圾收集器分类:
serial GC: 新生代收集算法,采用单线程、复制的方式执行GC,由于是单线程,适用于client模式,以及单CPU或者双CPU这种CPU资源很少的机器
serialOld GC: serial GC的老年代版本,采用标记-整理的方式进行老年代的回收,除了适用于client模式的jvm垃圾收集,还被用于server模式下的CMS收集器的备用收集器
parNew GC: 是serial GC的多线程版本,称为并行GC,目前是新生代收集器的首选,除了可以并行之外,还因为新生代收集器中只有它可以与CMS收集器配合使用
parallel Scavenge GC: 也是新生代收集器,也是采用复制算法,并行收集,那么它对于parNew GC的区别在于哪里呢,它的特点在于它的关注点与其他收集器不一样,在于程序的吞吐率上,所谓吞吐率就是 运行用户代码的时间/运行用户代码的时间+GC的时间,parallel Scavenge提供了两个参数用于精准控制吞吐率, -XX:maxGcPauseMillis :表示最大暂停时间,也就是告诉虚拟机尽量在此时间内回收完毕
-XX:GCTimeRatio: 指GC时间占总时间的比,也就是吞吐率的倒数
当然,这样并不意味着把maxGcPauseMillis设置的越小就越好,缩小maxGcPauseMillis是以程序吞吐率和新生代空间为代价的,当缩小时,虚拟机会自动调小新生代空间,这样时间是下来了,但是频率却变高了,吞吐率可能还会上升,所以这个值关键要适合
还有一个值得关注的参数是 -XX:+useAdaptiveSizePolicy 表示虚拟机自适应调整参数,只适用于parallel Scanvenge GC,配置该参数后就无需指定新生代大小,eden与s区比例以及晋升年龄了,虚拟机会自动调整至合适的大小以便达到一个合适的吞吐率
parallelOld GC: 是parallel Scavenge GC的老年代版本,采用标记整理的方式进行垃圾收集,jdk1.6才开始提供,在此之前parallel Scavenge GC就只能与Serial Old GC配合使用,它出现之后吞吐率优先才有了 一个名副其实的组合,也就是parallel Scavenge GC+parallelOld GC
CMS GC :其关注点在于缩短GC时的停顿时间,收集步骤分为:
初始标记(枚举GC Roots,需要暂停)
并发标记(根据GC Roots找到需要清理的对象,无需暂停,并发进行)
重新标记(对于那些在并发标记阶段引用情况发送变化的对象,重新进行标记,需要暂停)
并发清除(最终的清除阶段,不需要暂停)
最耗时的是并发标记和并发清除,都可以与用户线程并发执行,大大缩小了程序暂停时间。但是CMS也有几个明显的不足:
1.对CPU资源敏感,由于并发标记和并发清除都是与用户线程同步执行的,所以需要占用一部分本来可以用在用户程序的CPU资源
2.并发清除阶段还会产生新的垃圾,所以CMS不能像其他收集器一样等到老年代即将满了才开始收集,需要预留一部分空间给用户线程
3.CMS采用的是整理收集的方法,因此会产生空间碎片,可能就会造成前面说的剩余空间大但是无法进行大对象分配的问题,对此CMS收集器专门添加了-XX:+UseCMSCompactAtFullCollection表示在CMS收集顶不住的时候进行一次空间整理,而空间整理是无法并发进行的,需要较长的时间。还有一个参数-XX:CMSFullGcsBeforeCompact表示进行多少次不带压缩的垃圾收集就会进行一次带压缩的垃圾收集

垃圾收集参数总结:


MaxTenuringThreshold 参数表示新生代对象晋升到老年代的最大年龄,也就是经历的minorGC的次数,当对象经历的minorGC次数大于这个值时就会进入老年代,但是并不仅仅如此,虚拟机还可以进行 动态对象年龄判断 :如果Surivor区中某个年龄的对象占的内存大于Surivor区的一半,则大于或者等于该年龄的对象就会直接进入老年代
HandlePromotionFailure:在进行minorGC之前,会判断老年代的连续空间是否大于新生代总大小,如果小于就说明此次minorGC是有风险的,风险就是此次GC后晋升到老年代的对象大小大于老年代的剩余连续空间,会导致fullGC,此时会去查看HandlePromotionFailure参数是否允许担保失败,如果否则会先进行一次fullGC,如果是 则表示可以冒险,此时并不是直接进行GC而是再判断一下历次平均晋升对象大小是否小于老年代最大的连续空间大小,如果否也会先进行一次fullGC


猜你喜欢

转载自blog.csdn.net/wb_snail/article/details/80597221