定义:
JVM的垃圾收集都采用分代收集算法,就是将堆分为**新生代和老年代**,这样我们可以根据各个年代的特点选择 合适的垃圾回收算法
标记复制算法定义:
将内存分为大小相同的两块,每次只使用其中的一块,当进行垃圾回收时,将还存活的对象复制到另一块去,然后把使用的空间一次清理。
缺点:
每次只能使用一半的内存
优点:
避免内存碎片的产生
标记清除算法定义:
用GC Roots去标记存活的对象,统一回收所有未被标记的对象
缺点:
内存碎片产生 —— 标记清除后会产生大量不连续的碎片
效率不高 —— 如果需要标记的对象太多,效率不高
标记整理算法定义:
用GC Roots去标记存活的对象,将这些对象往一端移动,然后直接清除 端边界以外的内存
缺点:
效率不高
垃圾收集器垃圾收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现
垃圾收集器配合使用图:
Serial收集器参数:
-XX:+UseSerialGC(年轻代), -XX:+UseSerialOldGC(老年代)
定义:
一个单线程收集器,在GC过程中,必须暂停其他所有的工作线程( “Stop The World” ),直到GC结束
算法:
新生代采用复制算法,老年代采用标记-整理算法。
优点:
简单高效 —— 因为没有上下文切换,获得很高的单线程效率
缺点:
STW影响用户体验
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操作,在插入之前或者删除之前将 变更的引用记录下来。
使用:
增量更新 和 原始快照都用到了写屏障
例子:
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 | 读屏障 |



