标记清除算法
从字面意思上来看,就能看出这个算法有着两个操作,一个是标记,另一个是清除,先进行标记,再进行清除。
为了进行垃圾回收,我们首先要知道哪些是可以被回收的,所以‘标记’应运而生。
标记:通过根节点(GC Roots),把所有从GC Roots开始的对象进行标记,而后未被标记的对象就会被视为已经无用,也就是可以回收的对象。
清除:清除掉所有未被标记的对象。
适用场景
适用于对象存活率高的场景。
缺陷
会产生大量的内存碎片,每次清除原有对象的同时,就会把原有对象占用的内存空间给腾出来。
仅仅只是将空间腾出来的话,就可能会导致内存的不连续,从而产生大量的内存碎片。
当程序中有一个较大的对象想要放入内存时,需要占用一大段连续内存,这时候的内存中因为都是碎片,没有连续的内存,就会导致程序再去调用算法进行垃圾回收。
复制算法
该算法依然会去标记目前存活的对象,但与标记清除算法不同。
在标记之后,复制算法会将目前存活的对象复制+移动到另一块内存中,然后将原来的内存块全部清除掉。
这里的复制+移动,是要将原内存块中的对象顺序排列成连续性的,以留存出更大具有连续性的内存。
所以我们可以得出一个结论,就是复制算法必须要有两块内存才可以实施。
适用场景
适用于对象存活率低的场景,只有存活率够低,复制的时候所消耗的性能则会越低。
缺陷
缺点就是必须要有两块内存,这样就会导致本来可以任其获取的内存目前缩水了一半,这也是复制算法不能全部应用在虚拟机内存的原因。
移动的时候也会消耗一定的性能。
因为是复制+移动,所以不会留下内存碎片。
标记整理算法
标记整理算法,当然是先标记,再整理了。
该算法继续沿用了标记功能,在标记之后,先将存活对象都全部移动到内存的一端,然后根据最后一个存活对象的位置,将在其之后的所有空间清理掉。
适用场景
中规中矩,均可。
缺陷
并没有非常明显的缺点,主要还是因为解决碎片化和内存消耗过大的问题衍生出来的。
相对于标记清除算法,该算法不会产生大量碎片,是标记清除算法的改良版。
相对于复制算法,该算法不会无端占用过多的内存。
分代收集算法
该算法是目前Java虚拟机中使用的算法,主要是将前面几种算法归结在一起,然后在特定的情况下使用特定的算法。
具体如下:
还记得老年代占了多大的内存空间吗?
老年代中的对象存活率高,大对象也居多,内存占用打,又加上程序在运行时可能会存在大对象,所以这里不能产生过多的内存碎片,这些问题说明了不能使用标记清除算法和复制算法;so 老年代的垃圾回收要使用标记整理算法。
还记得新生代里面有哪些内存块吗?
分别是Eden、To Survivor、From Survivor三个内存空间,这里的两个Survivor区就是为了复制算法准备的。
加上新生代中存放的对象一般存活率很低,所以复制算法再合适不过了。