JVM_9 之 CMS、G1、算法

310 阅读6分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

CMS

1628903932.jpg

1628757822.jpg

  1. 初始标记:STW找到根对象,耗时较短
  2. 并发标记:应用线程和GC线程并发执行,将根对象引用的其它对象找出来,耗时较长。在此过程中,会出现之前不可用对象重新变成可用对象的情况。
  3. 重新标记:STW找到在并发标记过程中产生状态变化的对象,进行重新标记,耗时较短
  4. 并发清理:应用线程和GC线程并发执行,清理垃圾对象,在此过程中会重新产生新的垃圾(浮动垃圾),交给下次GC清理。

G1

参考文章:g1gc

  • 垃圾优先 Garbage First
  • G1是一种服务端应用使用的垃圾回收器,目标是用在多核、大内存的机器上,它在大多数情况下可以实现指定的GC暂停时间,同时还能保持较高的吞吐量。

G1:分而治之(逻辑分代、物理不分代)

1628905747.jpg

特点

  • 并发收集
  • 压缩空闲时间不会延长GC的暂停时间
  • 更易预测的GC暂停时间
  • 适用于不需要实现很高的吞吐量的场景

G1的内存区域不是固定的E或者O

GC算法的基本概念

  • Card Table(卡表)

由于做YGC时,需要扫描整个old区,判断某个对象是否被引用,效率非常低,如果有个old区Card Table中有对象指向Y区,就将它设为 dirty,下次扫描时,只需要扫描Dirty Card。在结构上,Card Table用BitMap来实现。

  • CSet = Collection Set

    • 一组可以被回收的分区的集合
    • 在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden空间、Survivor空间、或者老年代。CSet会占用不到整个堆空间的1%大小。
  • RSet = Remembered Set

    • 记录了其它 Region 中的对象到本 Region 的引用
    • RSet的价值在于:使得垃圾回收器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。
      1. RSet与赋值的效率
      • 由于RSet的存在,那么每次给对象赋引用的时候,就得做一些额外的操作
      • 指的是在RSet中做一些额外的记录(在GC中被称为写屏障)
      • 这个写屏障不等于内存屏障

1628908468.jpg

  • 阿里的多租户JVM

    • 每租户单空间
    • session based GC
  • 每个Region有多大

1628909278.jpg

  • 新老年代比例 5%-60%

    • 一般不用手工指定
    • 因为这是G1预测STW停顿时间的基准
  • humongous object(大对象)

    对象超过单个region的50% 或者 跨越多个region

G1中的MixedGC

-XX:InitiatingHeapOccupancyPercent 老年代占用空间达到整堆内存阈值

  • 默认值45%
  • 当old对象超过这个值,启动MixedGC
MixedGC过程
  • 初始标记 STW
  • 并发标记
  • 最终标记 STW(重新标记)
  • 筛选回收 STW(并行)

三色标记法

并发标记算法,在G1和CMS中使用

  • 白色:未被标记的对象
  • 灰色:自身被标记,成员变量未被标记
  • 黑色:自身和成员变量均已标记完成

1628921153.jpg 漏标:B->D断开,A->D

产生漏标需要同时满足两个条件:

  1. 标记进行时增加了一个黑到白的引用,如果不重新对黑色进行处理,则会漏标
  2. 标记进行时删除了灰色对象到白对象的引用,那么这个白对象有可能被漏标

解决漏标的两种方法:

增量更新 【CMS】

incremental update -- 增量更新 -- 关注引用的增加

把黑色重新标记为灰色,下次重新扫描属性

SATB 【G1】

SATB 【snapshot at the beginning】 -- 关注引用的删除

当B->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到

为什么G1使用SATB?

灰色->白色引用消失时,如果没有黑色指向白色,引用会被push到堆栈,下次扫描时拿到这个引用, 由于与RSet的存在,不需要扫描整个堆去查找指向白色的引用,效率比较高。

SATB配合RSet

CMS日志分析

执行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01

[GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

ParNew:年轻代收集器

6144->640:收集前后的对比

(6144):整个年轻代容量

6585 -> 2770:整个堆的情况

(19840):整个堆大小

[GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
	//8511 (13696) : 老年代使用(最大)
	//9866 (19840) : 整个堆使用(最大)
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
	//这里的时间意义不大,因为是并发执行
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
	//标记Card为Dirty,也称为Card Marking
[GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
	//STW阶段,YG occupancy:年轻代占用及容量
	//[Rescan (parallel):STW下的存活对象标记
	//weak refs processing: 弱引用处理
	//class unloading: 卸载用不到的class
	//scrub symbol(string) table: 
		//cleaning up symbol and string tables which hold class-level metadata and 
		//internalized string respectively
	//CMS-remark: 8511K(13696K): 阶段过后的老年代占用及容量
	//10108K(19840K): 阶段过后的堆占用及容量

[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
	//标记已经完成,进行并发清理
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
	//重置内部结构,为下次GC做准备

G1日志详解

[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
//young -> 年轻代 Evacuation-> 复制存活对象 
//initial-mark 混合回收的阶段,这里是YGC混合老年代回收
   [Parallel Time: 1.5 ms, GC Workers: 1] //一个GC线程
      [GC Worker Start (ms):  92635.7]
      [Ext Root Scanning (ms):  1.1]
      [Update RS (ms):  0.0]
         [Processed Buffers:  1]
      [Scan RS (ms):  0.0]
      [Code Root Scanning (ms):  0.0]
      [Object Copy (ms):  0.1]
      [Termination (ms):  0.0]
         [Termination Attempts:  1]
      [GC Worker Other (ms):  0.0]
      [GC Worker Total (ms):  1.2]
      [GC Worker End (ms):  92636.9]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.0 ms]
   [Other: 0.1 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.0 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.0 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
//以下是混合回收其他阶段
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000078 secs]
[GC concurrent-mark-start]
//无法evacuation,进行FGC
[Full GC (Allocation Failure)  18M->18M(20M), 0.0719656 secs]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]