G1

时间:2020-05-28
本文章向大家介绍G1,主要包括G1使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

g1收集器是一个面向服务端的垃圾收集器,适用于多核处理器,大内存容量的服务器系统;满足短时间gc停顿的同时达到一个较高的吞吐量;JDK7以上版本适用。

G1算法将堆分为若干个区域region,它任然属于分代收集器,不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或则Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另一个区域,完成清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩,不存在向CMS的内存碎片问题

G1设计目标

1、与应用线程同时工作,几乎不需要stop the world

2、采用复制算法,不产生内存碎片

3、GC停顿可控制

4、不牺牲系统吞吐量

5、gc不要求额外的内存空间

G1收集器堆结构

heap被划分为一个一个相等的不连续的内存区域(regions),每个region都有一个分代角色:eden、surivor、old;对于每个角色的数量并没有强制的限定,也就是说堆每种分代内存的大小,可以动态变化;G1的最大特点就是高效的执行回收,优先去执行哪些大量对象可回收的区域。

G1使用了gc停顿可预测的模型,来满足用户设定的gc停顿时间,根据用户设定的目标时间,G1会自动地选择哪些region要清除,一次清除多少个region;G1从多个region中复制存活的对象,然后集中放入一个region中,同时整理、清除内存。

G1vs CMS

CMS使用标记-清除算法,G1采用复制算法,前者会产生内存碎片

对比Parallel Scavenge、Parallel Old收集器,Parallel会对整个区域整理导致gc停顿时间会较长,而G1只是特定的整理几个region

G1并非一个实时的收集器,与Parallel Scavenge一样,对gc停顿时间的设置并不绝对生效,只是G1有较高的几率保证不超过设定的gc停顿时间。与之前的gc收集器对比,G1会根据用户设定的gc停顿时间,智能评估那几个region需要被回收可以满足用户的设定

G1重要概念

分区:G1采用了不同的策略来解决并行、串行和CMS收集器的碎片、暂停时间不可控制等问题,G1将整个堆分成相同大小的分区。

每个分区都可能是年轻代也可能是老年代,但是在同一个时刻只能属于某个代。年轻代、幸存代、老年代这些概念还存在,称为逻辑上的概念,这样方便复用之前的分代框架的逻辑

在物理上不需要连续,则带来了额外的好处,右得分区内垃圾对象特别多,有的分区内垃圾对象少,G1会优先回收垃圾对象特别多的分区,这样可以花费较少的时间来回收这些分区的垃圾,这也就是G1名字的又来,即首先收集垃圾最多的分区

依然是在新生代满了的时候,对整个新生代进行回收,整个新生代中的对象,要么被回收,要么晋升,至于新生代也采取分区机制的原因,则是因为这样跟老年代的策略统一,方便调整代的大小

G1还是一种带压缩的收集器,在回收哦老年代的分区时,时将存活的对象从一个分区拷贝到另一个可用分区,这个拷贝的过程就实现了局部压缩。

收集集合(CSet):一组可被回收的分区集合。在CSet中存活的数据会在gc过程中被移动到另一个可用分区,CSet中的分区可以来自eden空间、survivor空间、或者老年代

已记忆集合(RSet):RSet记录了其他region中对象引用本region中对象的关系,属于points-into结构(谁引用了我)。RSet的价值在于使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可

G1 GC是在points-out的card table之上再加了一层结构来构成prints-into RSet:每个region会记录下到底哪些别的region有指向自己的指针,而这些指针分别再哪些card的范围内。

这个RSet其实时一个hash table,key是别的region的起始地址,value是一个集合,里面的元素card table的index。举例来说,regionA的RSet里面有一项Key是regionB,value里面有index为1234的card,它的意思就是regionB的一个card里有引用指向regionA。所有对于regionA来说,该RSet记录的是points-into的关系,而card table任然记录了points-out的关系。

Snapshot-At-The-Beginning(SATB):SATB是G1再并发标记阶段使用的增量式的标记算法。

并发标记是并发多线程的,但是并发线程在同一时刻只扫描一个分区。

G1 GC模式

G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World

Young GC:选定所有年轻代里的Region。通过控制年轻代的region个数,即年轻代内存大小,来控制Young GC的时间开销

Mixed GC:选定所有的年轻代里的region,外加根据global concurrent marking统计的出收集收益高的若干个老年代region。在用户指定的开销目标范围内尽可能选择收益高的老年代region

Mixed GC:不是full GC,它只能回收部分老年代的region,如果Mixed GC实在跟不上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial Old GC(full GC)来收集整个GC heap。所有本质上G1不提供full GC的。

global concurrent marking

global concurrent marking的执行过程类似CMS,但是不同的是,G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节

global concurrent marking的中兴过程分为四部分:

  初始标记(initial mark, STW):它标记了从GC Root开始直接可达的对象。

  并发标记:这个阶段从GC Root开始堆heap中的对象进行标记,标记线程与应用线程并发执行,并且收集各个region的存活对象信息

  重新标记(remark, STW):标记哪些在并发标记阶段发生变化的对象,将被回收

  清理:清除空region(没有存活对象的),加入到free list

初始阶段共用了Young GC的暂停,这是因为他们可以复用root scan操作,所以可以说glogal concurrent marking是伴随Young GC而发生的

清理阶段只是回收了没有存活对象的region,所以它不需要STW

G1在运行过程中的主要模式

YGC:YGC在Eden充满时候触发,在回收之后所有之前属于Eden的区域全部变成空白,即不属于任何一个分区(eden、survivor、Old)

并发阶段

混合模式

Full GC1

G1参数

G1HeapWastePercent:在glogal concurrent marking结束之后,我们可以知道old gen region中有多少个空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会去检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC

G1MixedGCLiveThresholdPercent:old generation region中的存活对象占比,只有在此参数之下,才会被选入CSet

G1MixedGCCountTarget:一次glogal concurrent marking之后,最多执行Mixed GC次数

G1OldCSetRegionThresholdPercent:一次Mixed GC中能被选入CSet的最多old generation region数量

MaxGCPauseMillis:启动应用程序暂停时间一般情况这个值设置100ms到200ms,逐步调整大小到最优状态

Humongous区域

在G1中,还有一种特殊的区域,叫Humongous区域,如果一个对象占用空间达到或是超过了分区的50%,G1收集器就认为只是一个巨型对象。这些巨型对象,默认直接会被分配在老年代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了找到连续H区,有时候不得不启动Full GC

G1 Young GC

阶段1:跟扫描

  静态和本地对象被扫描

阶段2:跟新RS

  处理dirty card队列,更新RSet

阶段3:处理RS

  检测从年轻代指向老年代的对象

阶段4:对象拷贝

  拷贝存活对象到survivor/old区域

阶段5:处理引用队列

  软引用、弱应用、虚引用

Mixed GC

Mixed GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区,它的GC步骤分为两步:

  全局并发标记

  拷贝存活对象

三色标记算法

我们将对象分成三种类型:

  黑色:根对象,或者该对象与它的子对象都被扫描过

  灰色:对象本身被扫描过,但是还没有扫描完成对象中的子对象(它的field还没有被标记或标记完)

  白色:未被扫描对象,扫描完成所有对象之后,最终为白色不可达对象,即为垃圾对象

如果标记过程中,应用程序也在运行,那么对象的指针就可能发生改变,这样就可能造成:对象丢失问题。

如:A指向B,C,B指向D,当AC标记完成变成黑色,标记B是,指向逻辑B.d=null, C.d=D,此时就会造成D漏标,对于这种情况,G1使用SATB的方式,删除时候记录所有的对象,包括三个步骤:

  在开始标记的时候生成一个快照图,标记存活对象

  在并发标记的时候所有被改变的对象入队(在write barrier里把所有旧引用所指向的对象都变成非白的)

  可能存在浮动垃圾,将在下次被收集

G1混合式回收

G1到现在可以知道哪些老的分区可回收垃圾最多,当全局百年祭完成后,在某个时刻,就开始了Mixed GC。这些垃圾回收被称为“混合式”因为他们不仅仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的分区

混合式GC也是采用复制清理策略,当GC完成后,会重新释放空间

G1最佳实践

MaxGCPauseMillis:启动应用程序暂停时间一般情况这个值设置100ms到200ms,逐步调整大小到最优状态

不要设置新生代和老年代的大小

关注Evacuation Failure

  类似CMS晋升失败,堆空间的垃圾太多导致无法完成region之间的拷贝,于是退化成Full GC来做一次全局范围的垃圾收集

查看G1垃圾收集信息

/**
 * -verbose:gc -Xms10m -Xmx10m -XX:+UseG1GC 
 * -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:MaxGCPauseMillis=200m
 */
public class G1Test {
    public static void main(String[] args) {
        int size = 1024*1024;
        byte[] bytes_1 = new byte[size];
        byte[] bytes_2 = new byte[size];
        byte[] bytes_3 = new byte[size];
        byte[] bytes_4 = new byte[size];
        System.out.println(1111);
    }
}
D:\jdk1.8.0_65\bin\java.exe -verbose:gc -Xms10m -Xmx10m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:MaxGCPauseMillis=200m "-javaagent:E:\Program Files\JetBrains\IntelliJ IDEA 2018.1.2\lib\idea_rt.jar=56836:E:\Program Files\JetBrains\IntelliJ IDEA 2018.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk1.8.0_65\jre\lib\charsets.jar;D:\jdk1.8.0_65\jre\lib\deploy.jar;D:\jdk1.8.0_65\jre\lib\ext\access-bridge-64.jar;D:\jdk1.8.0_65\jre\lib\ext\cldrdata.jar;D:\jdk1.8.0_65\jre\lib\ext\dnsns.jar;D:\jdk1.8.0_65\jre\lib\ext\jaccess.jar;D:\jdk1.8.0_65\jre\lib\ext\jfxrt.jar;D:\jdk1.8.0_65\jre\lib\ext\localedata.jar;D:\jdk1.8.0_65\jre\lib\ext\nashorn.jar;D:\jdk1.8.0_65\jre\lib\ext\sunec.jar;D:\jdk1.8.0_65\jre\lib\ext\sunjce_provider.jar;D:\jdk1.8.0_65\jre\lib\ext\sunmscapi.jar;D:\jdk1.8.0_65\jre\lib\ext\sunpkcs11.jar;D:\jdk1.8.0_65\jre\lib\ext\zipfs.jar;D:\jdk1.8.0_65\jre\lib\javaws.jar;D:\jdk1.8.0_65\jre\lib\jce.jar;D:\jdk1.8.0_65\jre\lib\jfr.jar;D:\jdk1.8.0_65\jre\lib\jfxswt.jar;D:\jdk1.8.0_65\jre\lib\jsse.jar;D:\jdk1.8.0_65\jre\lib\management-agent.jar;D:\jdk1.8.0_65\jre\lib\plugin.jar;D:\jdk1.8.0_65\jre\lib\resources.jar;D:\jdk1.8.0_65\jre\lib\rt.jar;C:\Users\Administrator\Desktop\文件\zookeeper\jvm\out\production\jvm;E:\springBoot\repository\mysql\mysql-connector-java\5.1.45\mysql-connector-java-5.1.45.jar;C:\Users\Administrator\Desktop\文件\zookeeper\asm-3.2.jar;C:\Users\Administrator\Desktop\文件\zookeeper\cglib-nodep-2.2.jar com.jvm.g1.G1Test
2020-05-28T12:50:13.577+0800: [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0136177 secs]
   [Parallel Time: 1.2 ms, GC Workers: 8]
      [GC Worker Start (ms): Min: 129.5, Avg: 129.6, Max: 129.6, Diff: 0.1]
      [Ext Root Scanning (ms): Min: 0.2, Avg: 0.3, Max: 0.5, Diff: 0.3, Sum: 2.4]
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
         [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Object Copy (ms): Min: 0.0, Avg: 0.3, Max: 0.4, Diff: 0.4, Sum: 2.7]
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2]
         [Termination Attempts: Min: 1, Avg: 6.6, Max: 11, Diff: 10, Sum: 53]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.6]
      [GC Worker Total (ms): Min: 0.7, Avg: 0.7, Max: 0.8, Diff: 0.1, Sum: 6.0]
      [GC Worker End (ms): Min: 130.3, Avg: 130.3, Max: 130.3, Diff: 0.0]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.2 ms]
   [Other: 12.3 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.5 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.1 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 3072.0K(6144.0K)->0.0B(2048.0K) Survivors: 0.0B->1024.0K Heap: 4246.1K(10.0M)->2891.2K(10.0M)]
 [Times: user=0.00 sys=0.00, real=0.02 secs] 
2020-05-28T12:50:13.596+0800: [GC concurrent-root-region-scan-start]
2020-05-28T12:50:13.597+0800: [GC concurrent-root-region-scan-end, 0.0008334 secs]
2020-05-28T12:50:13.597+0800: [GC concurrent-mark-start]
2020-05-28T12:50:13.597+0800: [GC concurrent-mark-end, 0.0000456 secs]
2020-05-28T12:50:13.597+0800: [GC remark 2020-05-28T12:50:13.597+0800: [Finalize Marking, 0.0002264 secs] 2020-05-28T12:50:13.597+0800: [GC ref-proc, 0.0000785 secs] 2020-05-28T12:50:13.597+0800: [Unloading, 0.0005439 secs], 0.0015977 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
2020-05-28T12:50:13.599+0800: [GC cleanup 5021K->5021K(10M), 0.0003609 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
1111
Heap
 garbage-first heap   total 10240K, used 4939K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)
  region size 1024K, 2 young (2048K), 1 survivors (1024K)
 Metaspace       used 3283K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 359K, capacity 388K, committed 512K, reserved 1048576K

原文地址:https://www.cnblogs.com/zhanhaitao/p/12980077.html