G1垃圾收集器

1、定义

G1收集器是Java虚拟机的垃圾收集器理论进一步发展的产物
G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。
它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,具备了一些实时Java(RTSJ)的垃圾收集器的特征。
G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收
它能够极力地避免全区域的垃圾收集,之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根 据允许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的来由)。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获得最高的收集效率。

2、适用场景

同时注重吞吐量(Throughtput)和低延迟(Low latency),默认的暂停目标是200ms
超大堆内存,会将堆划分为多个大小相等的独立区域(Region)每个区域都是1248M,每个区都能独立的作为伊甸园、生存区、老年代。
整体上是标记+整理算法,不会产生内存碎片,两个区域之间是复制算法

3、相关的jvm参数

参数含义
-XX:+UseG1GC他的作用是开启G1垃圾收集器,在JDK1.8他不是默认的,所以需要开关。到了JDK9以后,就是默认打开的了
-XX:G1HeapRegionSize=size用来设置G1 region 的大小。 必须是2的幂(x次方),允许的范围是 1MB 至 32MB。这个参数的默认值, 会根据堆内存的初始大小动态调整,以便将堆内存切分为2048个左右的独立空间(region)。
-XX:MaxGCPauseMilllis=time设置GC的最大暂停时间。如果我们需要调优,在内存大小一定的情况下,我们只需要修改最大暂停时间即可

4、区域划分

在这里插入图片描述

  • G1收集器将整个Java堆划分成约2048个大小相同的独立Region块,每个Region块大小根据堆空间的实际大小而定,整体被控制在1MB到32MB之间,且为2的N次幂,即1MB, 2MB, 4MB, 8MB, 1 6MB, 32MB。2048MB 每个区都能独立的作为伊甸园、生存区、老年代。
  • 可以通过-XXG1HeapRegionSize设定。所有的Region大小相同,且在JVM生命周期内不会被改变。
  • G1虽然还保留着新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region (不需要连续)的集合。通过Region的动态分配方式实现逻辑_上的连续。
  • 一个region(分区)只能属于一个角色,有可能为eden区、S区、老年代等, E表示为Eden区、S区表示为S1,S0区,老年代O区
    空白的表示为未使用的分配的内存。
    H区存放巨型对象(在G1收集器中也有一个新的内存区域,称作为:Humongous (H)区(巨型对象),主要存放一些比较大的对象,一个对象大于region的一半时,称之为巨型对象,G1不会对巨型对象进行拷贝,回收时会考虑优先回收。)

4、G1的回收阶段

G1的运行过程与CMS大体一致,分为以下四个步骤:

1、是Young Collection,是对新生代的垃圾收集。
2、Young Collection + Concurrent Mark,即新生代的垃圾收集同时执行一些并发的标记。
3、Mixed Collection,即混合收集。
这三个阶段是循环的过程,刚开始是Young Collection,如果经过一段时间当老年代的内存超过阈值了,那么他就会Young Collection的同时进行并发的标记,之后就进行Mixed Collection,他就是会对新生代幸存区还有老年代都来进行一个规模较大的收集。混合收集结束后,等内存释放掉了,这时候伊甸园的内存都被释放掉,他就会再次进入Young Collection过程。

1、Young Collection

在这里插入图片描述

G1把对象划分为一个个空白区域,每个区域都可以独立作为伊甸园、幸存区和老年代。
当我们新创建了一些对象,会被分配到E伊甸园区域里
因为设置伊甸园的总的区域有大小限制,所以当这个伊甸园区域逐渐被占满,就会触发一次新生代的垃圾回收(Young Collection),新生代的垃圾回收会触发stw
新生代的垃圾回收会把这些幸存的对象以拷贝的算法放入幸存区(途中蓝色的S)
再工作一段时间,当幸存区的对象也比较多了,并且有的时候幸存区的一些对象的存活年龄超过一定时间,这时候又会触发一次新生代的垃圾回收,幸存区的一部分对象就会晋升到老年代(途中的橘色O),不够年龄的再次拷贝到另一个幸存区,即新生代的幸存对象也会复制到新的幸存区去。

2、Young Collection(新生代回收) + Concurrent Mark(并发标记)

垃圾回收时,对这些对象做初始标记和并发标记,初始标记是指找到那些根对象,而并发标记是指从根对象出发,顺着他的引用链去找其他的对象去标记。这个初始标记是在新生代GC时,世界暂停后,他就会对这些根对象做初始标记,所以初始标记不会占用我们并发标记的时间,因为它仅仅发生在新生代的垃圾回收时

老年代占用堆空间比例达到阈值时,这时候会发生并发标记,这个跟前面cms里的并发标记类似,他是并发执行的,在标记的同时不会影响到用户的工作线程,也就是不会stw。阈值可以JVM参数控制:-XX:InitiatingHeapOccupancyPercent=percent(默认45%,即老年代占用到整个堆空间的大约45%时,他就会进行并发标记)

3、Mixed Collection(混合收集)

  • 年轻代的gc只会回收年轻代的region。而mixed collection会同时回收年轻代和老年代的region。

  • 经过并发标记之后,gc就有了mixed collection所需要的必要信息:该不该回收,该回收哪些region,会优先收集那些垃圾最多的区域

  • 整个过程和年轻代的gc差不多。也是复制,清除。对于年轻代的,复制到survivor region或者提升到old region。对于年老代的region的对象,复制到年老代的region。

  • 最终标记(Remark)会STW,拷贝存活(Evacuation)会 STW

  • mixed collection不是一次性的,通常会发生好几轮(因为需要控制停顿时间,不能一次收集太多old region)。直到收集到足够多的年老代的region,也就是heap的用量低于45%。之后就会回到年轻代的gc。等到heap用量大于45%之后就会进入新的并发标记,然后多轮mixed collection。

5、Minor GC和Full GC有什么不同

  • Minor GC(新生代GC):指发生在新生代的垃圾收集动作,Java对象大多存活时间不长,所以Minor GC(采用复制算法)的发生会比较频繁,回收速度也比较
  • Full GC/Major GC(老年代GC标记-整理算法):指发生在老年代的GC,出现了Full GC,经常会伴随至少一次的Minor GC(不是必然的),Major GC的速度一般会比Minor GC10倍以上。

6、Young Collection 跨代引用(使用卡表,标记脏卡,提高遍历速度)

在这里插入图片描述

新生代的垃圾回收,首先要①找到根对象,然后②进行可达性分析,③再找到存活对象,④存活对象进行复制,复制到幸存区。
我们要通过根对象查找这种新生代的对象,需要先找根对象,根对象有一部分是来自于老年代的,而老年代存活对象比较多,如果我们去遍历整个老年代去找根对象,显然效率是非常低的。
因此采用的是一种卡表的技术,把老年代的区域(O)再进行细分,分成一个个的卡,每个卡大约是512k,如果这个老年代其中有一个对象引用了新生代的对象,那么对应的卡我们标记为脏卡,也许还有一个卡的对象引用了新生代,也把这个card标记为脏卡,这样的好处就是我将来做GC ROOT遍历时就不用去找整个老年代了,而是只需要去关注那些脏卡区域就可以了,这样就是减少了搜索范围,提高效率。
图里粉红色区域就是脏卡区,他们其中对象都引用了新生代区域中的对象。Remembered Set他会记录从外部对我的一些引用,就是记录都有哪些脏卡,将来对新生代(E)垃圾回收时,我就可以先通过Remembered Set知道它对应的那些脏卡,然后到这些脏卡区去遍历GC ROOT,这样就减少了GC ROOT的遍历时间。
这里就会有一个问题,我们需要标记这些脏卡,他是通过一个叫post-write barrier这样的写屏障,在每次对象的引用发生变更时,他都要去更新脏卡,即把卡表中的卡标记为脏卡,这个是异步操作,他不会立刻去完成这个脏卡的更新,他会把这个更新的指令放在一个脏卡的队列之中,将来由一个线程去完成这个脏卡的更新操作,这是我们新生代垃圾回收时,跨代引用时利用这种卡表和Remember Set的技术来加速了新生代的垃圾回收。

7、重标记(Remark)

在这里插入图片描述
黑色,处理完成的,灰色是正在处理的,白色会被当成垃圾进行回收

重标记是为了防止漏处理,在并发标记阶段当对象引用改变时,jvm会加入一个写屏障,引用改变,写屏障指令就会被执行,会将该对象加入一个队列中,将对象变为为处理状态,等到整个并发标记结束,进入重新标记阶段会STW, 对象出队列,进行标记

8、JDK 8u20字符串去重(放堆的内容一样的会去重)

-XX:+UseStringDeduplication,字符串去重,这个开关是默认打开的。
将所有新分配的字符串(底层是char[])放入一个队列
当新生代回收时,G1并发检查是否有重复的字符串
如果字符串的值一样,就让他们引用同一个字符串对象
注意,其与String.intern的区别
intern关注的是字符串对象
字符串去重关注的是char[]
在JVM内部,使用了不同的字符串标
优点与缺点
节省了大量内存
新生代回收时间略微增加,导致略微多占用CPU

9、JDK 8u40并发标记类卸载

从JDK 8u40版本开始,G1就会在所有对象①经过并发标记阶段后,他就能知道②哪些类不再被使用,这时候他就会去③尝试执行类卸载的操作。
卸载条件比较苛刻,首先这些①类的实例都被回收掉
第二就是②这个类所在的类加载器中的所有类都不在使用,这时候他就会把这个类加载器里面的所有类全部卸载掉。
虽然这些条件多少苛刻了一下,但如果是一些框架程序,这些框架程序很多都使用了自定义的类加载器,那么这种情况还是会发生的,就是一个类加载器中所有类和所有类的实例全部没人用了,那么G1就可以把他全部卸载掉
XX:ClassUnloadingWithConcurrentMark 启动类卸载功能,默认是启用的。

10、jdk8u60 回收巨型对象(占空间超region一半,优先回收,不拷贝)

G1划分区域时,其实有四种,除了伊甸园区、生存区、老年代区,还有一种是巨型对象区。
巨型对象,主要存放一些比较大的对象,一个对象大于region的一半时,称之为巨型对象,G1不会对巨型对象进行拷贝,回收时会考虑优先回收

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轩*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值