常见的垃圾回收算法
常见的垃圾回收算法如下:
1、引用计数算法
给对象添加一个引用计数器,每当一个地方引用它时,数据器加1;当引用失效时,计数器减1;计数器为0的即可被回收。
优点:实现简单,判断效率高
缺点:很难解决对象之间的相互循环引用(objA.instance = objB; objB.instance = objA)的问题,所以java语言并没有选用引用计数法管理内存
2、根搜索算法
Java和C#都是使用根搜索算法来判断对象是否存活。通过一系列的名为“GC Root”的对象作为起始点,从这些节点开始向下搜索,搜索所有走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时(用图论来说就是GC Root到这个对象不可达时),证明该对象是可以被回收的。
在Java中这些对象可以成为GC Root:
虚拟机栈(栈帧中的本地变量表)中的引用对象
方法区中的类静态属性引用的对象
方法区中的常量引用对象
本地方法栈中JNI(即Native方法)的引用对象
3、标记-清除算法
标记-清除算法是一种常见的基础垃圾收集算法,它将垃圾收集分为两个阶段
标记阶段:标记出可以回收的对象。
清除阶段:回收被标记的对象所占用的空间。
标记-清除算法主要有两个缺点,一个是标记和清除的效率不高,另一个从图中就可以看出,就是容易产生大量不连续的内存碎片,碎片太多可能会导致后续没有足够的连续内存分配给较大的对象,从而提前触发新的一次垃圾收集动作。
4、复制算法
为了解决标记-清除算法的效率不高的问题,产生了复制算法。它把内存空间划分为两个相等的区域,每次只使用其中一个区域。在垃圾收集时,遍历当前使用的区域,把存活对象复制到另一个区域中,最后将当前使用的区域的可回收的对象进行回收。
这种算法每次都对整个半区进行内存回收,不需要考虑内存碎片的问题,代价就是使用内存为原来的一半。复制算法的效率与存活对象的数目多少有很大的关系,如果存活对象很少,复制算法的效率就会很高。由于绝大多数对象的生命周期很短,并且这些生命周期很短的对象都存于新生代中,所以复制算法被广泛应用于新生代中。
5、标记-压缩算法
在新生代中可以使用复制算法,但是在老年代就不能选择复制算法,因为老年代对象存活率会较高,这样会有较多的复制操作,导致效率变低。标记-清除算法可以应用在老年代中,但是效率不高,在内存回收后容易产生大量内存碎片。因此就出现了一种标记-压缩算法,与标记-清除算法不同的是,在标记可回收的对象后将所有存活的对象压缩到内存的一端,使它们紧凑地排列在一起,然后对边界以外的内存进行回收,回收后,已用和未用的内存都各自一边。
标记-压缩算法解决了标记-清除算法效率低和容易产生大量内存碎片的问题,它被广泛应用于老年代中。
6、分代收集算法
分代收集算法会结合不同的收集算法来处理不同的空间,因此在学习分代收集算法之前我们首先要了解Java堆区的空间划分。Java堆区的空间划分在Java虚拟机中,各种对象的生命周期会有着较大的差别,大部分对象生命周期很短暂,少部分对象生命周期很长,有的甚至与应用程序以及Java虚拟机的运行周期一样长。因此,应该对不同生命周期的对象采取不同的收集策略,根据生命周期长短将它们放到不同的区域,并在不同的区域采用不同的收集算法,这就是分代的概念。现在主流的Java虚拟机的垃圾收集器都采用分代收集算法。Java堆区基于分代的概念,分为新生代和老年代,其中新生代再细分为Eden空间、From Survivor空间和To Survivor空间。因为Eden空间中的大多数生命周期很短,所以新生代的空间划分并不是均分的,HotSpot虚拟机默认Eden空间和两个Survivor空间的所占的比例为8:1。
根据Java堆区的空间划分,垃圾收集的类型分为两种,它们分别如下:
Minor Collection:新生代垃圾收集。
Full Collection:对老年代进行收集,又可以称作Major Collection,Full Collection通常情况下会伴随至少一次的Minor Collection,它的收集频率较低,耗时较长。
当执行一次Minor Collection时,Eden空间的存活对象会被复制到To Survivor空间,并且之前经过一次Minor Collection 并在From Survivor空间存活的仍年轻的对象也会复制到To Survivor空间。有两种情况Eden空间和From Survivor空间存活的对象不会复制到To Survivor空间, 而是晋升到老年代。一种是存活的对象的分代年龄超过-XX:MaxTenuringThreshold(用于控制对象经历多少次Minor GC 才晋升到老年代)所指定的阈值。另一种是To Survivor空间容量达到阈值。当所有存活的对象被复制到To Survivor空间,或者晋升到老年代,也就意味着Eden空间和From Survivor空间剩下的都是可回收对象。
这个时候GC执行Minor Collection,Eden空间和From Survivor空间都会被清空,新生代存活的对象都存放在To Survivor空间。接下来将From Survivor空间和To Survivor空间互换位置,也就是此前的From Survivor空间成为了现在的To Survivor空间,每次Survivor空间互换都要保证To Survivor空间是空的,这就是复制算法在新生代中的应用。在老年代则会采用标记-压缩算法或标记-清除算法。