JVM深入学习(二十一)-主流垃圾回收器G1

简介: G1(Garbage First) 是一款并行回收的,新生代/老年代都回收的全功能垃圾回收器

G1垃圾回收器

1.1 概述

G1(Garbage First) 是一款并行回收的,新生代/老年代都回收的全功能垃圾回收器

G1的思想是区域分代化,垃圾优先

区域分代化: 将堆内存分为一个一个的region,每个region可以是物理上不连续的空间,G1对region进行追踪,衡量每个region回收后的价值和回收所需时间(其实就是region回收的效率,回收后能清除较多空间的region优先级更高)

垃圾优先: 由于G1对垃圾回收的效率更加敏感,因此称G1是垃圾优先

G1的出现基于现代计算机资源的升级,cpu和内存不断增大,为了满足在资源充足的情况下的 低暂停时间和吞吐量 两者兼顾的目标

G1的目标就是: 在保证吞吐量的前提下,尽可能的减少暂停时间.

G1在jdk1.9中被设置为默认垃圾回收器,在jdk1.7/1.8中需要使用jvm参数启用

1.2 优点

  1. 并行和并发: G1同时兼顾并行和并发两种方式
  2. 分代收集: G1也属于分代收集算法,但是与传统分代不同的是,G1使用region作为分代的区域,region不在要求每个代在堆内存的连续性和数量.即每个region都是一个小的分代区域,但是region又不固定,可能一个Eden的region在完全回收后,下次这个region就变为老年代.
  3. 堆空间整理: 不存在内存碎片问题; region之间的回收是基于复制算法的,整体看G1的堆内存回收是基于标记-压缩算法,都不存在内存碎片问题,而且当堆空间越大,基于region的G1的优势越明显
  4. 可控制的暂停时间:
  1. G1可以设置一个停顿时间,尽可能的达到这个停顿时间.
  2. G1在回收region的时候是根据region的回收价值来排序,优先回收价值最高的region,就能保证最大的回收效率
  3. 拿CMS来对比,G1的回收效率可能比不过CMS最好的时候的延时停顿,但是相比于CMS的最差情况(串行回收),无疑好很多.
  1. 在其他垃圾回收器(ParNew/CMS)中,多线程的操作是基于jvm的内置线程的,而G1可以借助于应用程序线程.

1.3 缺点

  1. 在小内存(6-8g下)的场景下,效率并不一定超过CMS
  2. G1在垃圾回收上耗费的内存及保持程序运行的额外内存都要高于CMS


1.4 G1垃圾回收器相关jvm参数


1.4.1 指定使用G1垃圾回收器

-XX:+UseG1GC

启用G1垃圾回收器,在jdk9之后就不再指定了,默认都是G1

1.4.2 指定Region的大小

-XX:G1HeapRegionSize

指定每个Region的大小,一般为2的幂MB,例如: 2MB 4MB 8MB 16MB 32MB

默认值为堆内存的 1/2000

目的是将堆内存分为 2048 个区域.

例如: 当大小设置为1时, 堆内存就是2G 大小为2则堆内存为4G

1.4.3 指定期望的最大停顿时间

-XX:MaxGCPauseMillis

这个参数用于设置期望的最大停顿时间,jvm会尽可能保证达到这个停顿时间,但是不一定能每次回收都达到这个时间,只能尽可能保证高概率的达到这个停顿时间(90%)

参数默认值为 200ms

如果此参数修改的过小,可能会导致的结果: 比如设置为20ms,G1中有很多个region,默认值200ms可以回收10个region,但是20ms就只能回收1个region,如果此时内存占用速度较高,就会导致region回收的速度跟不上清理的速度,久而久之,当内存不够用时就会触发FullGC,反而增大了停顿时间,所以此参数修改要慎重.

1.4.4 指定并发的STW时的工作线程数

-XX:ParallelGCThreads

并发STW的垃圾回收线程数,最多为8,和CMS中的设置一样

1.4.5 指定并发标记的线程数

-XX:ConcGCThreads

设置并发标记的线程数,默认为-XX:ParallelGCThreads的1/4

1.4.6 指定触发垃圾回收的堆占用阈值

-XX:InitiatingHeapOccupAncyPercent

指定触发垃圾回收的java堆占用率阈值,超过此值出发垃圾回收 类似CMS中的参数

默认值为45

调整此参数策略也可以参考CMS中的解释.

1.4 jdk8中使用G1的步骤

目前jdk8应该还是企业主流版本,因此jdk8的垃圾回收器调优有以下几个步骤(G1):

  1. 使用jvm参数指定启用G1垃圾回收器
  2. 指定期望的最大停顿时间
  3. 指定堆内存大小

其他的就由jvm自动调整就可以,再细节的调优需要根据具体情况调整.

1.5 适用场景

  1. 大内存,多核处理器的硬件环境下的服务端应用(毕竟是保证吞吐量的前提)
  2. 即需要低延迟,有需要吞吐量的大内存应用
  3. 针对替换CMS的场景
  1. 超过一半的内存在活跃状态
  2. 对象分配频率和年代提升频率非常高
  3. GC停顿时间过长(0.5-1s)

1.6 region详解

G1使用region跳出了分代垃圾回收的大框架,开启了分区回收的新概念,其中region是最重要的一部分.

传统分代垃圾回收要求每代都是连续的内存空间

而G1将每个代对应的内存空间都拆分到一个一个region上,那么region其实就不再要求内存上的连续性了.

1.6.1 region的分类

G1中将region分为四类

  1. Eden 新生代的伊甸园区
  2. Survivor 新生代的幸存者区
  3. Old 老年代
  4. Humongous 存放大对象的区域

其中新生代/老年代的都不难理解,就是将粒度拆分的更细而已.

而新增了一个H区,这个类型主要用来存放大对象,大对象的定义为超过1.5个region

为什么新增这个大对象呢?

在原来的垃圾回收器中,如果一个新对象新生代放不下,那么就会直接进入老年代存放,但是如果这个大对象是一个生命周期很短的对象时,就存在问题,老年代的回收耗时要比新生代更加多.因此在G1中新增H区来处理大对象

注: 当一个H区存放不下一个大对象时,就必须寻找连续的H区来存放.

1.6.2 region的分配

首先 对G1来说,region的分配就是空闲列表的方式,因为region之间内存不连续,我们可以把region理解为一个个碎片,这个时候只能采用空闲列表的方式来分配.

对于region内部的分配来说就分为两种情况:

1.6.2.1 指针碰撞

region内部就是指针碰撞的方式来分配,region内部的垃圾回收算法是基于复制算法的,因此不存在碎片问题,直接使用指针碰撞就可以

1.6.2.2 TLAB

我们之前在堆内存的学习中看到过,堆的内存配置里有一小块是TLAB

主要是为了多线程的并发情况

region内部也存在TLAB,可以通过这样的方式分配内存.

1.7 G1垃圾回收的详细过程

1.7.1 主要环节

主要分为三个环节和一个后备环节

  1. 年轻代回收 YoungGC
  2. 老年代并发标记 Concurrent Mark
  3. 混合回收 Mixed STW
  4. 后备FullGC(单线程,独占式)

简单说明:

  1. 年轻代的回收其实跟其他gc没有区别,当年轻代内存不足时,开始回收年轻代的region,然后根据年龄计算是放到幸存者区还是进入老年代.这里指的年轻代是所有的年轻代的region合计内存,也就是说即使G1是分区的,但在分代的角度所有的region还是用分代的理念解释的. 此外,年轻代使用的是复制算法,因此是独占式的回收,需要STW.
  2. 老年代并发标记是标记可达对象的一个过程,这个过程是并发的,因此不影响用户线程执行,当堆内存达到阈值(默认45%)时,开始并发标记过程
  3. 在老年代并发标记完成后,就开始混合回收. G1的老年代回收与其他垃圾回收器不一样,不用回收整个老年代,他的回收也是根据region来的,我们在上文中也提到了一个最大停顿时间的参数,根据整个参数G1会调整回收region的数量,以达到设置的最大停顿时间(默认200ms),并且新生代也可以在整个环节进行回收(混合回收)


  1. 后备方案,就是当G1的老年代回收失败时,就会启用单线程的FullGC来做整个jvm的回收工作,以确保jvm的正常运行,出现FullGC的时候可能就等待时间较长.


1.7.2 记忆集 Remembered Set

记忆集记录了每个region被其他region引用的情况.

记忆集的出现主要是为了解决 跨代引用 问题

跨代引用是指: 新生代的对象被老年代的对象引用

如果出现了跨代引用,那么在垃圾回收的时候,怎么寻找GCRoots? 比如当新生代的对象被老年代的对象引用的时候,回收新生代的时候,怎么寻找该对象是否存在引用,将老年代全部遍历一遍也可以实现,但是开销太大.

记忆集就是为了解决这个问题的.

给每个region维护一个记忆集,记录每个region被其他region引用的情况,然后在垃圾回收的时候把记忆集作为GCRoots的一部分,就可以做到跨代引用然后进行垃圾回收.


1.7.3 YoungGC

  1. 当Eden内存不足时,会触发YoungGC
  2. YoungGC会回收Eden和Survivor区


当YoungGC开始时,首先STW,然后创建回收集(Collection Set),记录Eden和Survivor所有的内存分段.

YoungGC详细过程:

  1. 扫描GCRoots 获取根引用和Rset(记忆集)作为GCRoots
  2. 更新Rset 处理dirty card queue队列中的card,来更新Rset,保证Rset的最新;
  1. dirty card queue是在引用赋值的时候放入card的一个队列,这个card记录了对象的详细引用信息,用于保证Rset是最新的引用信息
  2. 使用dirty card queue的目的就是因为Rset存在多线程问题,如果在赋值的时候就直接更新Rset,可能存在锁的问题,开销更大.
  1. 处理Rset 识别Rset中被老年代对象引用的新生代对象,这些对象被认为是存活对象.
  2. 复制对象 采用复制算法将region放入到空闲的region中,这个过程中同时会考虑对象的年龄,如果对象的年龄达到阈值,则会进入老年代.
  3. 处理引用 处理软/弱/虚/终结器等引用信息.

YoungGC完成后,不存在内存碎片问题,且当复制对象完成后,原来的region会被作为空闲region放入空闲列表中等待使用.

1.7.4 老年代并发标记环节

这个过程类似CMS的并发标记过程,有一些区别,可以理解为G1采用了CMS的并发回收过程,且进行区域回收的改进.

具体流程如下:

  1. 初始标记 标记GCRoot直接引用的可达对象,这个阶段STW,并会触发一次YoungGC
  2. 根区域扫描 扫描Survivor区引用的老年代对象,并标记老年代中被引用的对象(视为可达对象),这个过程需要在YoungGC之前完成
  3. 并发标记 类似CMS,根据初始标记的结果进行并发标记,此时垃圾回收线程和用户线程并行执行,但是可能会被YoungGC打断,在并发标记阶段会同时计算该区域对象的存活率(可达对象占比),另外如果该区域所有对象都是垃圾对象,那么会直接进行清理,不用等待混合回收环节
  4. 再次标记 类似CMS,修正并发标记环节再次复活的对象.
  5. 独占清理计算 计算各个区域的存活对象占比,垃圾回收占比并排序,为下个环节混合回收做数据支持,因为计算需要准确性所以是STW的,不会真的回收垃圾
  6. 并发清理 清理完全空闲的区域.

1.7.5 混合回收

混合回收就是根据并发标记环节计算得出的回收占比,再根据最大停顿时间来回收一定数量的region,同时也会有YoungGC

混合回收有一些说明

  1. 默认情况下老年代要分8次回收(jvm参数-XX:G1MixedGCCountTarget控制)
  2. 混合回收的内容包括
  1. 1/8的老年代内存段
  2. Eden区
  3. Survivor区
  1. 老年代的1/8的内存段是根据垃圾占比排序出来的,优先回收垃圾占比高的内存分段.
  2. 老年代内存分段的回收也有一个阈值控制,默认65%(jvm参数-XX:G1MixedGCLiveThresholdPercent控制),当内存分段垃圾占比低于65%时,不会回收,因为回收的开销过大,复制算法存存活对象越多,开销越大.
  3. 老年代内存也可能进行不到8次,G1允许堆内内存有10%(jvm参数-XX:G1HeapWastePercent)的浪费,这就意味着如果进行7次或者更少的垃圾回收之后,堆内垃圾占比小于10%,那么就不会再次回收了,开销很大,回收的垃圾很少,划不来.

1.7.6 FullGC(并非G1每次垃圾回收必经阶段)

FullGC是单线程,独占式的垃圾回收过程,再开发和调优的过程中要尽量避免FullGC的出现.

FullGC的出现有两种情况:

  1. G1的回收都是基于复制算法的,将一个region复制到另外空闲的region,如果没有空闲的region的时候,就会进行FullGC
  1. 当堆内存过小的时候可能出现这个问题
  2. 当停顿时间设置的过小,而堆内存占用速度又过快的时候可能出现这个问题
  1. G1是存在并发标记阶段的,在这个阶段用户线程和垃圾回收线程交替执行,如果用户线程在这个时候没有足够的内存使用,就会触发FullGC.

1.8 G1的调优

首先G1在回收阶段(YoungGC和混合回收阶段)都是STW的,G1并没有并发回收的特性,但是在ZGC上可能会存在这个特性.

调优的几个注意点:

  1. 不要设置年轻代的大小 在使用-Xmn -XX:NewRatio等参数设置年轻代的大小后,年轻代的大小就被固定,不能由G1动态调节,而YoungGC又是独占式的回收,如果年轻代的大小固定,那么就会出现期望最大暂停时间参数被覆盖的问题,因为G1不能根据设置的期望最大暂停时间去调节年轻代的大小了.
  2. 暂停时间的设置不要太小,会影响到吞吐量
目录
相关文章
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
60 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
31 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
1月前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
29 4
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
1月前
|
存储 Java PHP
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
【JVM】垃圾回收机制(GC)之引用计数和可达性分析
58 0
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
52 0
|
3月前
|
缓存 监控 Java
"Java垃圾回收太耗时?阿里HBase GC优化秘籍大公开,让你的应用性能飙升90%!"
【8月更文挑战第17天】阿里巴巴在HBase实践中成功将Java垃圾回收(GC)时间降低90%。通过选用G1垃圾回收器、精细调整JVM参数(如设置堆大小、目标停顿时间等)、优化代码减少内存分配(如使用对象池和缓存),并利用监控工具分析GC行为,有效缓解了高并发大数据场景下的性能瓶颈,极大提升了系统运行效率。
73 4
|
3月前
|
算法 Java 应用服务中间件
探索JVM垃圾回收算法:选择适合你应用的最佳GC策略
探索JVM垃圾回收算法:选择适合你应用的最佳GC策略

热门文章

最新文章

  • 1
    Java面试题:描述Java垃圾回收的基本原理,以及如何通过代码优化来协助垃圾回收器的工作
    88
  • 2
    Java面试题:如何在Java中触发一次Full GC?请详细解释垃圾回收机制和知识
    372
  • 3
    Java面试题:在Java中,对象何时可以被垃圾回收?编程中,如何更好地做好垃圾回收处理?
    68
  • 4
    Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
    58
  • 5
    Java面试题:解释分代垃圾回收策略,并说明其优势
    51
  • 6
    Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
    49
  • 7
    Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
    68
  • 8
    Java面试题:请解释Java中的四种访问控制符及其作用范围,请解释Java中的垃圾回收机制及其工作原理,请解释Java中的并发工具包及其主要用途
    31
  • 9
    Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
    40
  • 10
    Java面试题:Java内存管理、多线程与并发框架的面试题解析与知识点梳理,深入Java内存模型与垃圾回收机制,Java多线程机制与线程安全,Java并发工具包与框架的应用
    77