基础:标记-清除算法
描述
先标记出所有需要回收的对象(图中深色区域),标记完后,统一回收所有被标记对象(留下狗啃似的可用内存区域……)。
不足
- 效率问题:标记和清理两个过程的效率都不高
- 空间碎片问题:标记清除后会产生大量不连续的内存碎片,导致以后为较大的对象分配内存时找不到足够的连续内存,会提前触发另一次 GC
解决效率问题:复制算法
描述
将可用内存分为大小相等的两块,每次只使用其中一块。当一块内存用完时,将这块内存上还存活的对象复制到另一块内存上去,将这一块内存全部清理掉。
不足
可用内存缩小为原来的一半,适合 GC 过后只有少量对象存活的新生代。
优化
- 新生代中的对象 98% 都是朝生夕死的,所以不需要按照 1:1 的比例对内存进行划分
- 把内存划分为:1 块比较大的 Eden 区和 2 块较小的 Survivor 区
- 每次使用 Eden 区和 1 块 Survivor 区,HotSpot 虚拟机默认比例为 8:1,也就是新生代每次可用内存为整体容量的 90%
- 回收时,将以上 2 部分区域中的存活对象复制到另一块 Survivor 区中,然后将以上两部分区域清空
- JVM 参数设置:
-XX:SurvivorRatio=8
表示Eden 区大小 / 1 块 Survivor 区大小 = 8
- 无法保证每次回收的存活对象另一块空闲的 Survivor 一定装得下,装不下还需要其他内存(老年代)来分配担保
解决空间碎片问题:标记-整理算法
描述
标记过程和标记清除算法一样,但是标记后不是对可回收的对象进行处理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
进化:分代收集算法
商业虚拟机的垃圾收集都采用分代收集算法。根据对象存活周期的不同将内存划分为几块,一般把 Java 堆分为新生代和老年代。
- 新生代:GC 过后只有少量对象存活 —— 复制算法
- 老年代:GC 过后对象存活率高,没有额外空间分配担保 —— 标记-清除、标记-整理算法