JVM垃圾回收之垃圾收集算法,程序员必须掌握的知识

时间:2022-07-24
本文章向大家介绍JVM垃圾回收之垃圾收集算法,程序员必须掌握的知识,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

垃圾算法分为哪几种

1.标记/清除算法 2.复制算法 3.标记/整理算法 jvm采用的是"分带收集算法"对不同的区域使用不同的算法

什么时候会触发垃圾回收

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有三种类型:Minor GC和Major GC和Full GC。 Minor GC:新生代回收GC Major GC: 老年代回收GC Full GC: 新年代 + 老年代的结合版GC

Minor GC

一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Minor GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC

对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Full GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC

分代收集算法

分带收集算法:是根据对象的存活周期的不同,将内存划分为几块,新生代和老年代,这样就可以根据不同的年代采用不同的收集算法。 新生代对象朝生夕死,对象数量何其之多,只要重点扫描这个区域,那么就可以大大提高垃圾收集的效率,另外老年代对象存储比较久,无需经常扫描,避免扫描所导致的开销

新生代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。

大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个)(YGC,年轻代垃圾回收),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

解释下,堆大小=新生代+老年代,新生代与老年代的比例为1:2,新生代细分为一块较大的Eden空间和两块较小的Survivor空间,分别被命名为from和to。

老年代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

复制算法

如果jvm使用了复制算法,一开始就会将可用内存分为两块,from域和to域, 每次只是使用from域,to域则空闲着。当from域内存不够了,开始执行GC操作,这个时候,会把from域存活的对象拷贝到to域,然后直接把from域进行内存清理。

1、当Eden区满的时候,会触发第一次young gc,把还活着的对象拷贝到Survivor From区;当Eden区再次触发young gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。
2、当后续Eden又发生young gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到From区域,并将Eden和To区域清空。
3、可见部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15,每次复制都会进入加一),最终如果还是存活,就存入到老年代

注意: 万一存活对象数量比较多,那么To域的内存可能不够存放,这个时候会借助老年代的空间。

标记/清除算法

注意:当对象的年龄达到了15 才会进入到老年代

该算法有两个阶段。
1. 标记阶段:找到所有可访问的对象,做个标记
2. 清除阶段:遍历堆,把未被标记的对象回收

优缺点: 优点

可以解决循环引用的问题
必要时才回收(内存不足时)

缺点

回收时,应用需要进行挂起,也就是stop the wrold
标记和清除效率不高,尤其要扫描的对象比较多的时候,会造成内存碎片(会导致明明有内存空间,但是由于不连续,申请稍微大一些的对象无法做到)

标记/整理算法

标记清除算法和标记压缩算法非常相同,但是标记压缩算法在标记清除算法之上解决内存碎片化

它的压缩算法:

任意顺序 : 即不考虑原先对象的排列顺序,也不考虑对象之间的引用关系,随意移动对象;
线性顺序 : 考虑对象的引用关系,例如a对象引用了b对象,则尽可能将a和b移动到一块;
滑动顺序 : 按照对象原来在堆中的顺序滑动到堆的一端。

优点:

解决了内存碎片的问题

缺点:

由于移动了可用对象我,需要重新更新引用

总结:

新生代:因为对象的存活期比较时间比较短,所以采用复制收集算法效率比较快
老年代:因为对象存活的时间比较长,没有额外的空间给他担保,故采用标记/清除算法和标记/整理算法