垃圾回收算法
前面介绍了如何去识别和标记垃圾,现在主要介绍,如何去回收(处理、删除)这些垃圾?
你也许会想,找到了直接删掉不就行了吗?
恭喜你,已经学会了一个很重要的算法。
标记-清除(Mark-Sweep)
标记-清除算法,主要分为两个步骤,标记 和 清除。标记,就使用之前说过的 可达性分析算法,即可进行标记。
就是我们根据 GC Roots 标记出存活对象,然后将垃圾对象进行清除即可。
就是这么简单。
但是这种算法主要有如下缺点:
会产生大量的内存碎片。内存空间不再连续,对于一些大对象连续的对象结构来说,无非是浪费内存的行为。
标记-复制
为了解决上面的问题,内存碎片的问题,有种算法叫做垃圾-复制算法出现了。
注意,我们将一个大内存分为 A B 两个部分。然后在一个内存中进行数据的存储。然后进行垃圾回收的时候,将标记出来的存活对象,直接复制到 B 空闲内存中,复制完成后,A 内存的全部空间都是可用的了。
在介绍其缺点之前,先明确一点:
只要移动了存活对象,相对而言,都会比较耗时,这个是由 Java 内存对象存储结构决定的。
这就是Java 存储对象的结构布局,首先栈中保存指向Java堆中的引用,然后Java堆中有着指向方法区的引用。
所以,如果我们移动了 Java 堆中的对象,那么Java栈中的引用就得进行变动。
像我们之前说的 标记-清除 算法就没这个问题,因为它不移动存活对象,只是单纯删除不用的对象。
回到我们的 标记-复制算法,如果每次复制的时候,有大量的对象需要复制,那么时间开销就很大了。
标记-整理(标记-压缩)
现在看下标记-整理算法,注意看上面的部分,当我们标记出存活对象的时候,我们就对他们进行移动整理,进而避免了垃圾碎片。
但也就像我们之前说的,因为涉及到存活对象的移动,所以也有比较大的时间开销。
分代收集算法
我们注意到上面三种算法,各自都有其缺陷和擅长的地方。
- 标记-清除适合少量垃圾存在的时候
- 标记-复制适合有大量垃圾存在的时候
- 垃圾-整理算法适合少量垃圾的时候
于是,因地制宜,按照对象的不同生命周期,提出了分代收集算法。
- 弱分代假说:绝大多数对象都是朝生夕灭的
- 强分代假说:熬过多次垃圾回收过程的对象,就越难消亡
于是,根据这种特点,就把内存大致分为老年代、年轻代。
年轻代中的对象,绝大多数很快就会消亡,所以存在大量的垃圾,可以采用标记-复制算法
老年代中的对象,很少对象会消亡,所以存在少量垃圾,可以采用标记-清除,和标记整理算法。
老年代 和 年轻代的更多细节,我们下次再说。