垃圾收集算法
分代收集理论
定义:
? JVM的垃圾收集都采用分代收集算法,就是将堆分为**新生代和老年代**,这样我们可以根据各个年代的特点选择 合适的垃圾回收算法
标记复制算法
定义:
? 将内存分为大小相同的两块,每次只使用其中的一块,当进行垃圾回收时,将还存活的对象复制到另一块去,然后把使用的空间一次清理。
缺点:
? 每次只能使用一半的内存
优点:
? 避免内存碎片的产生
标记清除算法
定义:
? 用GC Roots 去标记存活的对象,统一回收所有未被标记的对象
缺点:
标记整理算法
定义:
? 用GC Roots 去标记存活的对象,将这些对象往一端移动,然后直接清除 端边界以外的内存
缺点:
垃圾收集器
垃圾收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现
垃圾收集器配合使用图:
Serial收集器
参数:
-XX:+UseSerialGC(年轻代), -XX:+UseSerialOldGC(老年代)
定义:
? 一个单线程收集器,在GC过程中,必须暂停其他所有的工作线程( “Stop The World” ),直到GC结束
算法:
? 新生代采用复制算法,老年代采用标记-整理算法。
优点:
- 简单高效 —— 因为没有上下文切换,获得很高的单线程效率
缺点:
Parallel Scavenge收集器
参数:
-XX:+UseParallelGC(年轻代), -XX:+UseParallelOldGC(老年代)
-XX:ParallelGCThreads (指定收集线程数)
定义:
? 就是Serial收集器的多线程版本,关注点是吞吐量(高效率的利用CPU) 【使得GC时间尽可能短】
? JDK8默认的新生代和老年代收集器
算法:
? 新生代采用复制算法,老年代采用标记-整理算法。
CMS收集器
参数:
-XX:+UseConcMarkSweepGC(old)
定义:
? 是一种 获取最短回收停顿时间为目标【STW的时间】 收集器, 实现了让垃圾收集线程与用户线程并行工作
算法:
? 基于 “标记-清除”算法实现,用到 三色标记法的增量更新算法
优点:
- 并发收集
- 低停顿 —— STW时间比
Parallel Scavenge 长,但用户的感知短。
缺点:
- 浮动垃圾 —— 并发标记和并发清理时产生的垃圾没办法处理
- 内存碎片 ——
-XX:+UseCMSCompactAtFullCollection 参数设置做整理 - concurrent mode failure —— 并发模式失败,会转为
serial old 垃圾收集器来回收 【GC过程又有大对象移动到老年代】
- 通过参数
-XX:CMSInitiatingOccupancyFraction 调整当老年代使用达到某个值 [默认92%] 触发full GC ,调小一点,避免大对象移动老年代导致==并发模式失败==
过程:
过程 | 操作 |
---|
初始标记 | Stop The World 记下GC Roots直接引用的对象【直接,速度快!】 | 并发标记 | 从记下的直接引用对象开始遍历整个对象图 【不需要STW】 有可能漏掉标记 | 重新标记 | 对并发标记里面==漏标的对象进行标记== 【短时间的STW】 【基于三色标记的增量更新算法】 | 并发清理 | 开启用户线程,GC对未标记区域清扫 此时有新增对象标记为黑色,不做处理 | 并发重置 | 重置本次GC标记的数据【颜色】 |
优化
优化本质上就是让短期存活的对象尽量都留在survivor里,不要进入老年代,这样在minor gc 的时候这些对象都会被回收,不会进到老年代从而导致full gc
三色标记算法
定义:
? GC Roots可达性分析算法遇到的对象,标记为三种颜色:
- 黑色:本对象和引用都完全访问
- 灰色:本对象访问,但引用不全都访问
- 白色:本对象未被访问
漏标、多标问题
- 当CMS算法执行到这里,还没有来得及标记
D点
- 并发下,用户线程将A.d的引用指向
B.d ,B.d的引用置NULL ;
- 导致D未变色,作为垃圾对象【白色】被清除
解决问题
增量更新
定义:
? 当黑色对象A 插入新的指向白色对象D 的引用关系时,会保存新插入的引用;等并发标记结束后,在重新标记阶段将这些黑色对象A 作为根再次扫描 【重新标记会STW】
? 简化理解为, 黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了
原始快照
定义:
? 当灰色对象B 要删除指向白色对象D 的引用关系时, 会保存要删除的引用, 在并发标记结束后, 在重新标记阶段将这些灰色对象B 作为根再次扫描 【重新标记会STW】
写屏障
定义:
? 写屏障类似于AOP操作,在插入之前或者删除之前将 变更的引用记录下来。
使用:
增量更新 和 原始快照都用到了写屏障
例子:
/* 实现SATB 快照 */
void pre_write_barrier(oop* field) {
oop old_value = *field; // 获取旧值
remark_set.add(old_value); // 记录原来的引用对象
}
/* 实现增量更新 */
void post_write_barrier(oop* field, oop new_value) {
remark_set.add(new_value); // 记录新引用的对象
}
应用
下面垃圾收集器,针对漏标的处理方案:
垃圾收集器 | 方案 |
---|
CMS | 写屏障 + 增量更新 | G1 | 写屏障 + SATB | Shenandoah | 写屏障 + SATB | ZGC | 读屏障 |
|