1. 标记—清除算法
标记—清除算法是最基础的垃圾回收算法,后续的垃圾收集算法都是基于标记—清除算法进行改进而得到的。标记—清除算法分为“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
标记—清除算法有两个主要缺点:
- 效率问题,标记和清除的效率都不高;
- 空间问题,标记清除之后会产生大量不连续的内存碎片,导致程序在之后的运行过程中无法为较大对象找到足够的连续内存。
2. 复制算法
复制算法是将可用内存分成大小相等的两块,每次只使用其中的一块,当用完一块内存时,将还存活着的对象复制到另外一块内存,然后把已使用过的内存空间一次清理掉。
复制算法解决了效率问题。由于每次都是对整个半区进行内存回收,因此在内存分配时不需要考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可。
复制算法的优点是实现简单,运行高效,缺点是将内存缩小为了原来的一半,以及在对象存活率较高时复制操作的次数较多,导致效率降低。
3. 标记—整理算法
标记—整理算法是根据老年代的特点提出的。标记过程与标记—清除算法一样,但后续步骤不是直接回收被标记的对象,而是让所有存活的对象都向一端移动,然后清除边界以外的内存。
4. 分代收集算法
分代收集算法根据对象的存活周期不同将内存划分为多个区域,对每个区域选用不同的垃圾回收算法。
一般把 Java 堆分为新生代和老年代。
- 在新生代中,大多数对象的生命周期都很短,因此选用复制算法。
- 在老年代中,对象存活率高,因此选用标记—清除算法或标记—整理算法。
Java 堆可以分成新生代和老年代,新生代又可以细分成 Eden 区、From Survivor 区、To Survivor 区等。