• G1 垃圾回收器


     引用:

    1. G1垃圾收集器详解_fyygree的博客-CSDN博客

    2. 深入解析G1垃圾收集器与性能优化_铁锚的博客-CSDN博客

    3. 垃圾回收器之 G1 垃圾回收器_嘿,鱼骨头^O^的博客-CSDN博客

    G1是一款增量式的分代垃圾收集器

    G1 物理上不分代,默认会将整个内存区域分为2048个region,region的大小为1MB-32MB,且为2的N次幂。适合内存大的jvm。 region可分为四种类型,且region并不是固定的,运行时会改变

    • Eden Space 新生代

    • Survivor Space 存活区

    • Old Generation 老年代

    • humongous 存储大对象,如果超过0.5个region,就放入humongous

      对于堆中的大对象,默认直接会被分配到老年代,但是如果它是一个短期存在的大对象, 就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区, 它用来专门存放大对象。如果一个H区装不下一个大对象,那么G1会寻找连续的H区来 存储。为了能找到连续的H区,有时候不得不启动Full GC。 G1的大多数行为都把H区 作为老年代的一部分来看待。

      img

    G1尽可能多的从有空闲的region中回收内存,同时到达预期的暂停时间指标。

    card table (卡表)

    在这里插入图片描述

    老年代的每个region是有card组成的, 每个card 的大小是大约是512k。如果老年代的对象引用了新生代的对象,我们就把这个card标记为脏card,这样根据GC roots 找对象的时候就不用遍历整个老年代了,只在脏card中寻找就可以了。减少扫描范围,提升效率

    RememberSet(记忆集)

    在这里插入图片描述

    新生代中每个region中都有个RememberSet(记忆集),简称Rset,用来记录哪个card指向这个region的引用。通过这种数据结构,G1就可以进行增量式回收内存,而不用扫描整个堆内存,因为只要扫描Rset,就可以知道哪些跨区的引用指向这个Region,从而对这些region进行回收。

    G1的垃圾回收模式

    • young GC:只回收新生代

      会收集eden区和上一次gc使用的存活区,采用复制的方式转移到新的Region中,如果达到分代年龄或者其他条件,会转移到老年代中

      默认老年代占用到整个堆空间的大约45%时,它就会进行这个并发标记

    • mixedGC:回收一部分新生代和老年代

      当老年代的内存超过一个阈值。并发标记完毕之后,会从 young GC模式切换到mixed GC,除了回收eden区和上一次gc使用的存活区,还会选择一部分的老年代进行回收。在进行老年代回收时依然采用复制算法,因为复制算法执行的快。经过多次混合模式的垃圾回收,很多老年代的region已经被处理过了,就会切换到young GC

      tips:

      如果你的回收速度是高于新的用户线程产生的垃圾的速度的时候,也就是我的回收速度比你新产生的垃圾快,来得及打扫,这个时候还不叫 Full GC,这个时候还是处于并发垃圾收集的阶段,虽然重新标记和数据拷贝的过程还会有暂停,但是这个暂停时间还是相对很短的,这还称不上 Full GC,那 G1在什么时候才会发生 Full GC呢,就是当你的垃圾回收的速度跟不上垃圾产生的速度了,新产生的垃圾比你的回收速度快了,这个时候并发收集就失败了,就跟以前的 CMS 类似,这时候它就会退化一个串行的收集,串行收集这时候就会叫Full GC了,特别的慢,当然也会更长时间的 STW 导致响应时间变长。

    标记的各个阶段

    • 初始标记。stw,扫描GC roots

    • 并发标记。根据GC roots扫描整个堆,找到存活的对象

    • 重新标记。stw,通过写屏障+SATB(原始对象快照)的方式标记对象

      它是在pre-write barrier写屏障技术在对象引用改 变前把这个对象加入到了一个队列,并且表示它是未被处理的,这个队列的名称叫 satb_mark_queue,将来的 Remark 阶段就可以配合这个队列来对这些对象进行进一步的判断,

    • 并发清楚。

    G1垃圾收集器的运行过程:

    ①. G1 GC的垃圾回收过程主要包括如下三个环节:

    • 年轻代GC (Young GC)

    • 老年代并发标记过程 (Concurrent Marking)

    • 混合回收(Mixed GC)

    (如果需要,单线程、独占式、高强度的Full GC还是继续存在的。它 针对GC的评估失败提供了-种失败保护机制,即强力回收。)

    img

    顺时针young gc -> young gc + concurrent mark-> Mixed GC顺序,进行垃圾回收

    ②. 应用程序分配内存,当年轻代的Eden区用尽时开始年轻代回收过程;*G1的年轻代收集阶段是一个*并行(多个垃圾线程)的独占式收集器。在年轻代回收期,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收。然后从年轻代区间移动存活对象到Survivor区间或者老年区间,也有可能是两个区间都会涉及

    ③. 当堆内存使用达到一定值(默认45%)时,开始老年代并发标记过程

    ④. 标记完成马上开始混合回收过程。对于一个混合回收期,G1 GC从老年区间移动存活对象到空闲区间,这些空闲区间也就成为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,G1的老年代回收器不需要整个老年代被回收,一次只需要扫描/回收一小部分老年代的Region就可以了。同时,这个老年代Region是和年轻代一起被回收的。

    ⑤. 举个例子:一个Web服务器,Java进程最大堆内存为4G,每分钟响应1500个请求,每45秒钟会新分配大约2G的内存。G1会每45秒钟进行一次年轻代回收,每31个小时整个堆的使用率会达到45%,会开始老年代并发标记过程,标记完成后开始四到五次的混合回收

    G1的可选过程:Full GC

    • G1的初衷就是要避免Fu1l GC的出现。但是如果上述方式不能正常工作,G1会停止应用程序的执行(Stop-The-World) ,使用单线程的内存回收算法进行垃圾回收,性能会非常差,应用程序停顿时间会很长。

    • 要避免Full GC的发生,一旦发生需要进行调整。什么时候会发生Full GC呢? 比如堆内存太小当G1在复制存活对象的时候没有空的内存分段可用,则会回退到full gc, 这种情况可以通过增大内存解决。

    • 导致G1Full GC的原因可能有两个: .

      1. 回收的时候没有足够的to-space来存放晋升的对象

      2.并发处理过程没完成空间就耗尽了
  • 相关阅读:
    MySQL基础练习,涉及建库、见表、约束、修改表结构等,超详细
    每日一题 416 分割等和子集(01背包)
    VisualStudio(VS)设置程序的版本信息(C-C++)
    猿创征文 | Docker实战:Linux环境安装Tomcat安装步骤
    概要了解postman、jmeter 、loadRunner
    python深入理解super
    最简单的git图解(git stash)
    Texas Instruments 在 GitHub 的官方主页
    Day27.组合总和II、分割回文串
    nginx 同一个端口支持http和https配置
  • 原文地址:https://blog.csdn.net/l577125882/article/details/127970491