简述 G1、ZGC
- G1 之前都是将内存看做两块整体(Young 和 Old 区)。
- G1 是将内存分成一小块一小块的 分而治之,一部分在运行线程时,另一部分在进行回收。
- ZGC 是 G1 分块有大有小。
CMS 有两个缺点,一是产生碎片,二是产生浮动垃圾。它的最大问题是在触发 FullGC 时是单线程的,所以太慢了。
G1
G1 又叫 Garbage First,意思是当决定要进行垃圾回收时,它先进行 垃圾最多 的 Region 的回收。
G1 进行的是分而治之来处理的。
image.pngG1 和 CMS 都用的是并发收集,他们的算法也一样都是 三色标记算法。
ZGC 和 Shenandoah 用的算法是 颜色指针。
并发标记算法G1
概念
- CSet,Collection Set,指一组可被回收的分区的集合。
- RSet,Remember Set,在每一个 Region 中都有一个 Hash Set 记录了其他 Region 对象对本 Region 的引用。
- Card Table,在 YGC 时需要扫描整个 OLD 区的话效率会很低,所以有了 Card Table 的概念,它将 Old 区分为不同的 Card 组成一个 Table。如果有一个 OLD 区的 Card Table 中有对象指向 Y 区,就将它设为 Dirty,用 Bitmap 来实现,这样下次只需要扫描 Dirty Card 就可以找到它。
三色标记法
黑 -> 灰 -> 白(由黑色向白色延伸)
- 黑色表示自身和成员变量均被标记完成。
- 灰色表示自身被标记完了,但我所引用的对象还未标记。
- 白色是指自己还没有被标记。
漏标
只有在下面两个条件同时满足都会发生漏标:
- Remark 过程中黑色对象的引用指向了白色对象
- 与此同时,灰色对象对白色对象的指向丢失了
所以,解决漏标只需要打破其中一个条件即可。所以解决方法有两种,
一种是跟踪 A->D 的增加;
另一种是跟踪 B->D 的消失。
其分别为:
- Incremental Update,增量更新,关注引用的增加,当 A->D 发生时,把黑色重新标记为灰色。(CMS 用了它)
- SATB,Snapshot At The Beginning,在开始时进行一个快照,当 B->D 消失的时候,把这个 引用 推到 GC 的堆栈,保证 D 还能被扫描到。(G1使用这种)
SATB 这种形式,会在 GC 中创建一个栈,这个栈只存灰色对象指向白色对象的 引用,注意是 引用(
->
)。
G1 为什么要用SATB
因为前者会重新扫描 A。而后者只需要在栈中重新扫描就可以找到 D(都是重新标记阶段)。因为 RememberSet 的存在,在 RSet 中没有人用到 D 就可以认为 D 为垃圾。即它不需要再去扫描整个堆,所以效率更快。
RSet 会记录别人的引用指向我这里面的所有引用。
写屏障,它的意思就是由于 RSet 的存在,每次给对象赋引用的时候,做一些额外的操作,这就是写屏障(概念很无聊)。
G1 有 FGC 吗
有 FGC,当内存不够用时,就会进行 FGC。
G1 触发 FGC 了该怎么办
应该尽量避免触发 FGC。
- 扩内存
- 增加 CPU
- 调低 MixGC 的阈值。(MixGC 就是一套完整的 CMS)
图示三色标记法: https://making.pusher.com/golangs-real-time-gc-in-theory-and-practice/
网友评论