JVM工作原理与实战(二十七):堆的垃圾回收-G1垃圾回收器

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了G1垃圾回收器、G1垃圾回收器的回收方式、G1垃圾回收器执行流程、垃圾回收器的选择等内容。

一、垃圾回收器

垃圾回收器是Java虚拟机(JVM)中的重要组件,负责自动管理内存,回收不再使用的对象所占用的空间。了解垃圾回收器的种类、工作原理以及如何根据应用场景选择合适的垃圾回收器,对于提高应用程序的性能和稳定性至关重要。

垃圾回收器通过自动检测和回收不再被引用的对象,以释放内存空间,避免内存泄漏。为了实现这一目标,垃圾回收器采用了一系列算法来识别和回收无用对象。这些算法主要包括标记清除复制标记整理分代垃圾回收等。

垃圾回收器分为年轻代和老年代,它们各自负责不同生命周期的对象的回收。除了G1垃圾回收器外,其他垃圾回收器必须成对组合使用,以确保整个堆内存的有效管理。


二、G1垃圾回收器介绍

G1垃圾回收器(Garbage-First Garbage Collector)是JDK 9及之后版本的默认垃圾回收器。它的设计目标是将Parallel Scavenge和CMS两种垃圾回收器的优点相结合,以提供更好的性能表现。Parallel Scavenge关注吞吐量,允许用户设置最大暂停时间,但会减少年轻代可用空间的大小。相比之下,CMS关注暂停时间,但在吞吐量方面会有所下降。而G1垃圾回收器旨在解决这些问题,以实现更高效和可预测的垃圾回收性能。

G1垃圾回收器的设计特点:

  • 支持巨大的堆空间回收,并保证较高的吞吐量:通过将堆划分为多个相等的区域(Region),G1能够更有效地管理内存,并在回收过程中优先处理含有大量垃圾的对象区域。
  • 支持多CPU并行垃圾回收:G1采用并发收集方式,充分利用多核CPU的计算能力,提高垃圾回收的效率。这使得它在处理大规模数据和高负载应用程序时表现出色。
  • 允许用户设置最大暂停时间:通过调整相关参数,用户可以控制垃圾回收过程中的最大停顿时间,以满足应用程序的性能需求。

G1垃圾回收器采用了独特的内存管理策略,将整个堆内存划分为多个大小相等的区域,称为Region。这些Region不需要连续存储,提供了更大的灵活性。根据应用程序的需求,这些Region可以配置为Eden区(伊甸园区)、Survivor区(幸存者区)和Old区(老年代),以满足不同的回收需求。与之前的垃圾回收器相比,G1的出现改变了传统的内存管理方式。在G1之前,内存结构一般是连续的,而G1通过将堆内存划分为多个Region,打破了这种连续性。这种设计使得G1能够更好地适应现代计算机系统的内存布局和分配需求,提高了内存利用率和应用程序的性能。

image.gif

在G1垃圾回收器中,Region的大小是通过堆空间大小除以2048来计算得到的,也可以通过参数-XX:G1HeapRegionSize=来明确指定Region的大小。需要注意的是,Region size必须是2的指数幂,其取值范围是从1M到32M。

-XX:G1HeapRegionSize=

image.gif

G1垃圾回收器采用复制算法进行垃圾回收,该算法将活跃对象从一个内存区域复制到另一个内存区域,从而实现内存空间的回收。这种算法确保了内存碎片化最小化,提高了空间利用率和应用程序的性能。

G1垃圾回收器的优点:

  • 延迟可控性:对于较大的堆空间,如超过6G的堆,G1垃圾回收器能够保持较低的延迟,确保应用程序的响应性和性能。
  • 内存碎片最小化:G1通过复制算法进行垃圾回收,避免了内存碎片的产生,从而提高了内存的利用率和应用程序的性能。
  • 并发标记的SATB算法:G1使用高效的并发标记的SATB算法,该算法在标记阶段对应用程序的影响较小,确保了高吞吐量。
  • 多CPU并行处理:G1充分利用多核CPU的计算能力,通过并行处理提高垃圾回收的效率,特别是在处理大规模数据和高负载应用程序时。

G1垃圾回收器在吞吐量、延迟控制和内存管理方面具有显著优势。它适用于处理大规模数据和高负载应用程序的场景,尤其适用于对延迟敏感的应用程序和需要高效利用内存资源的应用。但是G1垃圾回收器在JDK 8之前还不够成熟,在JDK 8最新版本和JDK 9及更高版本中,建议默认使用G1作为垃圾回收器,可以使用-XX:+UseG1GC参数来启用G1垃圾回收器,JDK9之后默认不需要启用。

-XX:+UseG1GC

image.gif

三、G1垃圾回收器详解

1.G1垃圾回收器的回收方式

G1垃圾回收器主要采用了两种垃圾回收方式:年轻代回收(Young GC)和混合回收(Mixed GC)。

年轻代回收(Young GC):主要针对Eden区和Survivor区中不再使用的对象进行回收。这个过程会导致应用程序线程暂停(Stop-The-World)。G1垃圾回收器提供了一种灵活的机制,允许开发人员通过参数-XX:MaxGCPauseMillis=n(默认值为200毫秒)来设定每次垃圾回收时的最大暂停时间。这个参数的设置有助于优化应用程序的性能,确保垃圾回收过程对应用程序的影响降至最低。

-XX:MaxGCPauseMillis=n

image.gif

混合回收(Mixed GC)

G1垃圾回收器的混合回收包括以下步骤:

  1. 初始标记(Initial Mark):这一步主要标记GC Roots引用的对象为存活。GC Roots是垃圾回收的起始点,通常是活跃的对象。
  2. 并发标记(Concurrent Mark):在这一阶段,垃圾回收器会并发地遍历堆中的对象图,将初始标记阶段标记为存活的对象引用的对象也标记为存活。
  3. 最终标记(Final Mark or Remark):此步骤会再次检查并标记在并发标记阶段可能被漏标的对象,确保所有存活的对象都被正确地标记。同时,任何不再关联的对象也会被标记。
  4. 并发清理(Cleanup):这一步骤将存活的对象复制到其他Region,确保没有内存碎片的产生。G1垃圾回收器通过这种方式优化内存使用,并提高后续的垃圾回收效率。

image.gif

G1垃圾回收器对老年代的清理策略是选择存活度最低的区域进行回收,这样可以高效地回收内存,这也是G1(Garbage first)名称的由来。在清理阶段,G1使用复制算法,确保内存碎片的最小化。这种混合回收策略允许G1在不影响应用程序性能的情况下有效地管理Java堆的内存。

案例(回收红色区域)

image.gif

FULL GC:当G1垃圾回收器在执行清理阶段时,如果发现没有足够的空闲Region来存放需要转移的对象,就会触发Full GC,它会暂停所有的用户线程并使用单线程执行标记整理算法进行内存整理。这种操作对应用程序的性能影响较大,因此应尽量避免。为了预防Full GC的发生,开发者应确保分配给应用程序的堆内存有适当的预留空间,避免堆内存过度使用。这样可以减少Full GC的发生频率,并提高应用程序的稳定性和性能。

2.G1垃圾回收器执行流程

年轻代回收:

  • 1.对象分配与判断:新创建的对象首先会被放置在Eden区。G1垃圾回收器通过监控年轻代的使用情况,当判断年轻代区域已满(超过60%容量)时,触发Young GC。
  • 2.存活对象标记:在执行Young GC时,G1首先会精确地标记出Eden和Survivor区域中的存活对象。
  • 3.对象复制与区域清空:根据预设的最大暂停时间和其他配置参数,G1选择某些区域,将存活对象复制到一个新的Survivor区(对象的年龄加1),并清空这些区域。


  • 4.性能记录与优化:在执行Young GC过程中,G1垃圾回收器会记录每次回收时每个Eden区和Survivor区的详细耗时数据。这些数据为下次回收提供了宝贵的参考,帮助G1更精确地计算出在给定的最大暂停时间内可以回收的Region数量。例如,如果配置的-XX:MaxGCPauseMillis为n(默认200),每个Region的回收耗时为40ms,那么在一次回收中,G1最多能处理4个Region。
  • 5.循环与移动:后续的Young GC过程与之前相似,Survivor区中的存活对象会被移动到另一个Survivor区。
  • 6.老年代与Humongous区:当某个对象的年龄达到预设阈值(默认15)或其大小超过一个Region的一半时,该对象会被移入老年代。这些老年代被称为Humongous区。例如,在堆内存为4G、每个Region为2M的环境中,任何超过1M的对象都会被放入Humongous区。如果对象过大,可能会跨越多个Region。


混合回收:

  • 7.触发条件与处理:随着时间的推移,老年代中会出现多个区域。当总堆占有率达到预设阈值(-XX:InitiatingHeapOccupancyPercent默认45%)时,G1会触发混合回收(Mixed GC)。这种回收会处理所有年轻代和部分老年代的对象以及大对象区。混合回收采用复制算法来完成,确保高效的内存回收。
-XX:InitiatingHeapOccupancyPercent

image.gif

3.垃圾回收器的选择

对于JDK 8及更早版本:

  • ParNew + CMS:这个组合关注垃圾回收时的暂停时间。ParNew作为年轻代回收器,而CMS用于老年代回收。这种组合适合对暂停时间有严格要求的场景。
  • Parallel Scavenge + Parallel Old:这个组合主要关注吞吐量。Parallel Scavenge用于年轻代,Parallel Old用于老年代。这种组合适合对吞吐量有较高要求的场景。
  • G1(JDK 8之前不建议):尽管G1在JDK 8之前并不推荐使用,但在JDK 8及更早版本中,如果堆大小较大并且关注暂停时间,可以考虑使用G1。

对于JDK 9及更高版本:

  • G1:随着G1的日趋成熟,从JDK 9开始,G1已成为默认的垃圾回收器。因此,建议在生产环境中使用G1,以充分利用其性能和功能。

总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了G1垃圾回收器、G1垃圾回收器的回收方式、G1垃圾回收器执行流程、垃圾回收器的选择等内容,希望对大家有所帮助。

相关文章
|
4天前
|
存储 算法 Java
先有JVM还是先有垃圾回收器?
是先有垃圾回收器再有JVM呢,还是先有JVM再有垃圾回收器呢?或者是先有垃圾回收再有JVM呢?历史上还真是垃圾回收更早面世,先有垃圾回收再有JVM。下面我们就来刨析刨析JVM的垃圾回收~
13 0
先有JVM还是先有垃圾回收器?
|
4天前
|
自然语言处理 前端开发 Java
深入浅出JVM(六)之前端编译过程与语法糖原理
深入浅出JVM(六)之前端编译过程与语法糖原理
|
3天前
|
Java 数据库连接 Spring
K8S+Docker理论与实践深度集成java面试jvm原理
K8S+Docker理论与实践深度集成java面试jvm原理
|
4天前
|
存储 算法 Java
深入浅出JVM(十八)之并发垃圾收集器G1
深入浅出JVM(十八)之并发垃圾收集器G1
|
4天前
|
安全 算法 Java
深入浅出JVM(十三)之垃圾回收算法细节
深入浅出JVM(十三)之垃圾回收算法细节
|
4天前
|
存储 算法 Java
深入浅出JVM(十二)之垃圾回收算法
深入浅出JVM(十二)之垃圾回收算法
|
4天前
|
算法 Java PHP
JVM 的垃圾回收机制以及垃圾回收算法的详解
JVM 的垃圾回收机制以及垃圾回收算法的详解
10 0
|
4天前
|
存储 缓存 算法
深入浅出JVM(二)之运行时数据区和内存溢出异常
深入浅出JVM(二)之运行时数据区和内存溢出异常
|
4天前
|
存储 Java
深入理解Java虚拟机:JVM内存模型
【4月更文挑战第30天】本文将详细解析Java虚拟机(JVM)的内存模型,包括堆、栈、方法区等部分,并探讨它们在Java程序运行过程中的作用。通过对JVM内存模型的深入理解,可以帮助我们更好地编写高效的Java代码,避免内存溢出等问题。
|
4天前
|
Java Linux Arthas
linux上如何排查JVM内存过高?
linux上如何排查JVM内存过高?
579 0