JVM - 垃圾回收算法概述

290 阅读2分钟

既然选择了远方,即使天寒地冻,路遥马亡,我本就一无所有,又有何惧。

标记清除法

标记清除算法工作分为 2 个阶段。 第一个阶段,先根据 GC Root 标记 可达对象 第二阶段,将不可达对象,直接清除。

算法缺点:会产生大量的内存碎片

工作原理如下图所示:

复制算法

复制算法思想:

  1. 将内存一分为二,每次只使用其中一块。
  2. 发生垃圾回收时,将存活的对象复制到另一块未使用的内存
  3. 清空使用的内存块中的对象,两者角色互换,完成垃圾回收。

复制算法,用于新生代。java 借鉴复制算法,将新生代内存划分为 eden、from、to。默认比例为 8:1:1eden, from 存活的对象会被放入 to 中,eden, from 不可达对象会被清空。清空后, from、to 角色互换

算法前提:新生代对象朝生夕死

工作原理如下图所示:

标记压缩法

标记压缩法思想:

  1. GC Root 可达性分析 标记可达对象
  2. 将可达对象统一放至内存一端
  3. 清理边界外的空间

标记压缩不会产生内存碎片

工作原理如下图所示:

分代算法

JVM 将对象分为新生代和老年代。 新生代使用复制算法回收垃圾;老年代使用标记压缩或标记清除回收垃圾。

新生代对象大多朝生夕死,正常来说 ygc 频率高,速度快。如果 老年代对象引用了新生代对象,那么就需要扫描老年代对象。因此会造成 ygc 效率低下,需要全堆扫描。 JVM 引入 卡表(card table)数据结构 来解决 老年代对象引用新生代对象,造成 ygc 效率低下 问题。

卡表

JVM 通过卡表,记录老年代指向新生代的引用。 卡表为比特位数组,每个比特位可以用于表示老年代某一区域中所有对象是否持有新生代引用。0 表示没有持有,1表示持有。在 GC Root 扫描时,只需扫描卡表位为 1 的老年代空间即可,避免全堆扫描,提升了 ygc 效率。

分区算法

分区算法将整个堆空间分成连续的不同小区间,每一个小区间独立使用,独立回收。分区算法的好处是,可以控制一次 GC 回收的区间,即控制 GC 回收时间