JVM垃圾回收算法

342 阅读4分钟

找到垃圾的方法

1、引用计数:无法解决循环引用的问题

2、根可达性算法:CG root 包括 线程栈变量、静态变量、常量池、JNI指针(调用C或C++用到的本地方法对象)

清除算法

1、标记-清除 Mark - sweep 适合存活对象较多的。容易产生碎片,扫描两遍

2、拷贝 copy 适合存活对象较少的。浪费内存,移动复制对象,需要调整引用,扫描一遍

3、标记-压缩 Mark - compact 不容易产生碎片、不浪费内存,但 扫描两遍,移动对象

分代模型

除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型

G1是逻辑分代,物理不分代

除此之外不仅逻辑分代,而且物理分代

栈上分配

线程私有小对象

无逃逸

线程本地分配(TLAB)

本地线程大小占用Eden的1%

小对象

多线程的时候不用竞争Eden就可以申请空间

常见的垃圾回收器

1、serial 和 serial old

2、parallel 和 parallel old

3、parNew 和 CMS

4、G1

5、ZGC 和 Shenandoah

CMS (并发)

初始标记(只标根节点) -> 并发标记 ->重新标记(标记并发标记产生的新对象)-> 并发清理

CMS的问题

1、内存碎片,碎片太多,会用Serial Old 去清理老年代碎片

2、浮动垃圾(第二次回收)

并发标记算法:三色标记

垃圾收集器跟内存大小的关系

  1. Serial 几十兆
  2. PS 上百兆 - 几个G
  3. CMS - 20G
  4. G1 - 上百G
  5. ZGC - 4T - 16T(JDK13)

算法

CMS四个阶段

1、初始标记

2、并发标记

3、重新标记(并发标记阶段可能垃圾对象变成非垃圾对象)

4、并发清理

G1特点

  • 并发收集
  • 压缩空闲空间不会延长GC的暂停时间
  • 更容易预测GC暂停的时间
  • 适用不需要实现很高的吞吐量的场景,但响应很快
  • CSet(collection Set)存储一组可被回收的分区集合
  • RSet(RememberdSet)记录着两个不同的region相互引用的信息,每个region里都有一个RSet保存着被其他region里对象引用的信息
  • 由于RSet的存在,每次给对象赋引用的时候,需要维护RSet,在维护的过程被称为GC中的写屏障GC中的写屏障不等于内存屏障
  • G1新老年代是动态的,不要手动知道,这是G1预测停顿时间的基础

G1有FGC?

  • G1有FGC,当对象分配特别快,GC线程回收不完
  • G1的FGC在java 10 以前是串行的,之后是并行的

如果G1产生FGC,应该怎么办?

扩内存

提高CPU性能

降低mixedGC触发的阈值,让mixedGC提早发生(默认45%)

MixedGC就是一套完整的CMS

三色标记(CMS和G1)

三色就是三种状态

分别是黑(完成标记,只包括自己以及和自己相邻的节点,三级节点不算)、灰(完成一半标记,只标记自身了)、白(未标记)

关于漏标

在并发标记阶段,(如果灰色下面的白色标记与灰色断开,并且这个白色又与黑色相连)。那么就会产生漏标,因为黑色已经标记完成,不会再次扫描

解决漏标的方法

因为漏标有两个必要条件(如果灰色下面的白色标记与灰色断开,并且这个白色又与黑色相连)

所以破坏其中之一即可解决

1、如果(白色又与黑色相连)把黑色重新标记为灰色即incremental update--批量更新,下次重新扫描(CMS解决方案)

2、如果(灰色下面的白色标记与灰色断开)把灰色与白色的引用保留到GC的堆栈中,这样白色还能被GC扫描到。其实就是灰白程序中无引用,但是GC给做了一份引用,等重新扫描的时候,扫描引用集合就可以了。SATB(snapshot at the beginning,留一份快照)G1的解决方案

为什么G1用SATB,不用incremental update?

扫描次数变少了,如果黑色变为灰色,那么灰色还需要再次扫描一遍

下次扫描时,由于有RSet的存在,不需要扫描整个堆去查找白色引用

颜色指针(ZGC)