深入理解JVM中的G1垃圾收集器原理、过程和参数配置

news2024/12/22 12:03:02
码到三十五 : 个人主页

心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !

在Java虚拟机(JVM)中,垃圾收集(GC)是一个自动管理内存的过程,旨在回收不再使用的对象所占用的内存空间。G1垃圾收集器(G1 GC)是JVM中的一种重要垃圾收集器,尤其适用于需要低延迟和可预测停顿时间的大型应用程序。本文将深入探讨G1垃圾收集器的工作原理、关键特性和性能优化建议。

目录

    • 一、G1收集器概述
      • 主要特点
    • 二、G1分区划分
    • 三、为什么G1收集器需要设计巨型对象
    • 四、G1收集器的回收过程
    • 五、G1的两种回收策略
      • 5.1 Young GC(新生代回收)
      • 5.2 Mix GC(混合回收)
    • 六、跨代引用和RSet(记忆集)
    • 七、性能优化建议
    • 八、G1核心配置参数
    • 九、结语

一、G1收集器概述

G1收集器(Garbage-First Garbage Collector,简称G1 GC)是Java虚拟机(JVM)中的一种垃圾收集器,专为服务器端应用设计,特别适用于具有多核处理器和大内存的机器。G1 GC在JDK 7u4版本中被正式推出,并且在JDK 9中成为默认的垃圾收集器。它的主要目标是在满足高吞吐量的同时,尽可能缩短垃圾收集造成的停顿时间。

主要特点

  1. 并行与并发:G1 GC能够充分利用多核处理器的优势,通过并行执行垃圾收集任务来提高效率。同时,它的大部分工作都是与应用线程并发执行的,从而减少了停顿时间。
  2. 分区域收集:G1 GC将整个堆内存划分为多个大小相等的独立区域(Region),这些区域在逻辑上是连续的,但在物理内存上可能不是连续的。每个Region都可以扮演Eden区、Survivor区或Old区等角色。这种设计使得G1 GC能够更加灵活地进行内存管理和垃圾收集。
  3. 优先回收垃圾最多区域:G1 GC通过跟踪每个Region中的垃圾堆积情况,并根据回收价值和成本进行排序,优先回收垃圾最多的Region。这种策略有助于最大限度地提高垃圾收集的效率。
  4. 可预测的停顿时间:G1 GC通过建立一个可预测的停顿时间模型,允许用户明确指定在一个特定时间片段内,垃圾收集所造成的停顿时间不得超过某个阈值。这使得G1 GC非常适合需要严格控制停顿时间的应用场景。
  5. 使用标记-整理算法:在整体上,G1 GC使用标记-整理算法来回收内存,以减少内存碎片的产生。但在两个Region之间进行垃圾收集时,它则采用标记-复制算法。这种组合策略有助于兼顾内存利用率和垃圾收集效率。

二、G1分区划分

G1收集器的分区划分是其核心特性之一,它允许G1更灵活、高效地管理内存和执行垃圾回收:

  1. 基本思想
    G1收集器将整个Java堆划分为多个大小相等、独立的区域,这些区域被称为“Region”。每个Region的大小可以根据堆空间的实际大小而定,通常在1MB到32MB之间,且必须是2的N次幂。这意味着Region的大小可以是1MB、2MB、4MB、8MB、16MB或32MB。默认情况下,整个堆空间被划分为约2048个这样的Region。

  2. 分区类型
    G1的Region可以根据其用途和状态分为不同类型。主要包括:

    • 自由分区(Free Heap Region, FHR):这些Region当前没有包含任何对象,是空闲的,可以用于新的对象分配。
    • 新生代分区(Young Heap Region, YHR):这些Region被划分为新生代,包括Eden区和Survivor区。新生代分区主要用于存储新创建的对象。
    • 大对象分区(Humongous Heap Region, HHR):专门用于存储大对象。在G1中,只要对象的大小超过了一个Region容量的一半,就被认为是大对象。这些对象会被直接分配到Humongous Region中,且每个大对象都单独占用一个或多个连续的Humongous Region。
    • 老年代分区(Old Heap Region, OHR):这些Region被划分为老年代,用于存储长时间存活的对象。

在这里插入图片描述

  1. 分区的管理和回收
    G1收集器通过维护一个优先列表来跟踪各个Region中的垃圾堆积情况和回收价值。在垃圾回收过程中,G1会根据这个列表优先回收价值最大的Region。这种策略使得G1能够更有效地利用处理器资源,并最大限度地减少垃圾回收造成的停顿时间。

  2. 优点
    G1的分区划分带来了几个显著优点。首先,它允许更细粒度的内存管理,提高了内存的利用率。其次,通过优先回收垃圾最多的Region,G1能够保持较高的吞吐量并缩短停顿时间。最后,G1的分区策略使其能够很好地适应不同的内存大小和垃圾回收需求。

三、为什么G1收集器需要设计巨型对象

G1收集器需要设计巨型对象(Humongous Objects)主要是出于对内存管理和垃圾收集效率的考虑。在G1收集器的设计中,整个堆内存被划分为多个大小相等的区域(Region),每个Region用于存放对象。然而,有些对象的大小可能会超过一个Region的容量,这就引出了巨型对象的概念。

巨型对象是指那些大小超过了一个Region容量50%以上的对象。由于这些对象太大,无法完整地存放在一个Region中,因此需要特殊处理。G1收集器通过引入巨型对象的概念,并为之设计专门的存储和管理机制,确保了这些大对象能够被有效地管理和回收。

具体来说,巨型对象在G1中被直接分配到特殊的Humongous Region中,每个巨型对象可以独占一个或多个连续的Humongous Region。这样做的好处是可以避免由于对象跨Region存储而导致的复杂性和性能开销。同时,G1收集器还会针对巨型对象进行特殊的垃圾回收策略,以提高垃圾收集的效率和整个系统的性能。

此外,巨型对象的设计也考虑到了应用的实际情况和需求。在实际应用中,往往存在一些需要占用大量内存的大对象,如大型的数组、数据结构等。如果不对这些大对象进行特殊处理,它们可能会对整个垃圾收集器的性能和内存利用率造成负面影响。因此,G1收集器通过设计巨型对象及其管理机制来应对这一挑战。

综上所述,G1收集器需要设计巨型对象主要是为了更有效地管理大内存对象,提高垃圾收集效率和整个系统的性能。

四、G1收集器的回收过程

G1收集器的回收过程主要包括以下几个步骤:

  1. 初始标记(Initial Marking)
    这个过程是STW(Stop-The-World)的,但通常耗时非常短。它标记出从GC Roots直接可达的对象,作为后续垃圾收集的基础。

  2. 并发标记(Concurrent Marking)
    在初始标记完成后,G1 GC会进入并发标记阶段。这个阶段与应用程序线程并发执行,通过递归地追踪所有可达的对象,并将它们标记为存活。这个过程是并发的,因此不会阻塞应用程序的执行。

  3. 最终标记(Final Marking)
    为了处理在并发标记过程中新产生的对象引用关系,G1 GC会执行一次短暂的STW的最终标记。这个阶段确保所有在并发标记阶段漏掉的对象都被正确标记。

  4. 筛选回收(Live Data Counting and Evacuation)
    在这个阶段,G1 GC会根据每个Region的垃圾堆积情况和回收价值进行排序,并选择性地回收部分Region中的垃圾对象。回收过程包括将存活的对象从一个Region复制或移动到另一个Region,并更新相关的引用。这个过程也是并发的,旨在最大限度地减少停顿时间。同时,这个阶段可能会涉及到对象的整理和压缩,以减少内存碎片。

此外,G1收集器还采用了分区(Region)的方式来管理内存,每个Region都被标记了不同的状态(如Eden、Survivor、Old等)。这种设计使得G1能够更灵活地进行内存分配和垃圾回收,从而提高了整体的效率和性能。

值得注意的是,G1收集器还提供了两种主要的垃圾回收模式:Young GC和Mixed GC。Young GC主要负责回收新生代中的垃圾对象,而Mixed GC则负责回收新生代和部分老年代中的垃圾对象。这两种模式都是根据堆内存的使用情况和GC的触发条件来自动选择的。

五、G1的两种回收策略

G1垃圾收集器是Java虚拟机(JVM)中的一个重要组件,它提供了两种主要的垃圾回收策略:Young GC(新生代回收)和Mix GC(混合回收)。这两种策略在回收对象和回收区域上有所不同,但都是为了提高垃圾回收的效率,减少停顿时间,从而提升应用程序的性能。

5.1 Young GC(新生代回收)

Young GC主要负责回收新生代中的对象。新生代通常包含新创建的对象,这些对象更有可能在短时间内变成垃圾。Young GC的执行过程相对较快,因为它只涉及新生代中对象的扫描和回收。

在Young GC过程中,Eden区和Survivor区的存活对象会被复制到另一个Survivor区或者晋升到老年代。这个过程是Stop-The-World(STW)的,意味着在回收过程中,应用程序的所有线程都会被暂停。但是,由于新生代中的对象通常较少,因此这个暂停时间通常较短,对应用程序的性能影响也较小。

5.2 Mix GC(混合回收)

Mix GC则是G1收集器特有的回收策略,它不仅回收新生代中的所有Region,还会回收部分老年代中的Region。这种策略的目标是在保证停顿时间不超过预期的情况下,尽可能地回收更多的垃圾对象。

在Mix GC过程中,首先会进行全局并发标记(global concurrent marking),这个过程是并发的,与应用程序线程同时执行,用于标记出所有存活的对象。然后,在回收阶段,G1会根据标记结果选择收益较高的部分老年代Region和新生代Region一起进行回收。这个选择过程是基于对Region中垃圾对象的数量和回收价值的评估。

与Young GC不同,Mix GC的停顿时间可能会更长,因为它涉及到对老年代中对象的扫描和回收。但是,由于Mix GC能够回收更多的垃圾对象,因此它通常能够更有效地释放内存空间,减少垃圾堆积对应用程序性能的影响。

六、跨代引用和RSet(记忆集)

在垃圾收集过程中,跨代引用或跨Region引用是一个需要特别注意的现象:

  1. 跨代引用的概念
    在垃圾收集领域,跨代引用指的是不同代际之间的对象相互引用。在G1收集器中,由于堆被划分为多个Region,跨代引用通常表现为跨Region引用。年轻代指向老年代的引用在垃圾收集中不是主要问题,因为即使年轻代的对象被清理,程序仍然可以正常运行,且未被标记到的老年代对象会在后续的Major GC中被回收。

  2. 老年代指向年轻代的引用问题
    当存在老年代指向年轻代的引用时,情况就复杂了。在Minor GC阶段,我们不能简单地清理年轻代中的对象,因为老年代中可能还有对象持有对这些对象的引用。为了解决这个问题,我们需要一种机制来跟踪这些跨Region的引用。

  3. RSet(记忆集)的作用
    RSet正是为了解决这个问题而设计的。它的主要作用是记录哪些Region中的老年代对象有指向年轻代的引用。在GC时,通过扫描这些Region中的RSet,我们可以快速识别出需要保留的年轻代对象,从而避免扫描整个老年代,显著提高了垃圾收集的效率。RSet的实现本质上是一种哈希表,其中Key是Region的起始地址,Value是一个集合,存储了卡表的索引号。

RSet(RememberedSet)是一个非常重要的数据结构,用于记录并跟踪其他Region指向当前Region中对象的引用。在G1收集器的分区模型中,由于堆内存被划分为多个独立的Region,对象之间的引用关系可能跨越不同的Region。为了能够在垃圾收集过程中正确地识别和处理这些跨Region的引用,G1引入了RSet的概念。

每个Region都有一个与之关联的RSet,用于记录其他Region中指向该Region内对象的引用信息。当发生对象引用关系变化时,G1会更新相应的RSet,以确保垃圾收集的准确性。在垃圾收集过程中,G1会利用RSet来快速确定哪些Region之间存在引用关系,从而避免不必要的全堆扫描,提高垃圾收集的效率。

RSet的实现通常涉及一些优化技术,如使用位图(Bitmaps)或压缩表(CompressedTables)来紧凑地存储引用信息,以减少内存占用和提高访问速度。此外,G1还采用了一些策略来维护RSet的一致性,如在并发标记阶段使用写屏障(Write Barriers)来拦截并更新跨Region的引用。

  1. 减少YGC时的扫描开销
    由于新生代的垃圾收集通常很频繁(即YGC),如果每次都需要扫描整个老年代来确定是否有对新生代的引用,那么开销将会非常大。通过RSet的跟踪机制,我们可以精确地知道哪些老年代Region中的对象引用了新生代对象,从而只扫描这些Region,大大降低了YGC时的扫描开销。

  2. 卡标记(Card Marking)技术与卡表(Card Table)
    HotSpot JVM为了更高效地处理老年代到新生代的引用问题,采用了卡标记技术。具体来说,它使用了一个称为卡表(Card Table)的数据结构来辅助标记过程。堆空间被划分为一系列的卡页(Card Page),每个卡页对应卡表中的一个标记项。当发生对老年代到新生代引用的写操作时,通过写屏障(Write Barrier)机制来更新卡表中对应的标记项。这样,在GC时,我们只需要扫描那些被标记为dirty的卡页所对应的Region即可快速找到所有老年代到新生代的引用关系。

七、性能优化建议

  1. 合理设置堆大小:根据应用程序的内存需求和硬件资源,合理设置JVM的堆大小。过大的堆可能会导致长时间的垃圾收集停顿,而过小的堆则可能导致频繁的垃圾收集。

  2. 调整停顿时间目标:通过调整G1的停顿时间目标(-XX:MaxGCPauseMillis参数),可以平衡垃圾收集的效率和应用程序的响应时间。在需要低延迟的场景中,可以设置较短的停顿时间目标。

  3. 启用并行垃圾收集线程:通过增加并行垃圾收集线程的数量(-XX:ParallelGCThreads参数),可以提高垃圾收集的效率。然而,过多的线程可能会导致系统资源的竞争和额外的开销,因此需要谨慎调整。

  4. 优化对象分配和晋升策略:通过优化对象的分配和晋升策略,可以减少新生代和老年代之间的对象流动,从而降低垃圾收集的开销。例如,可以考虑使用对象池、缓存等技术来减少临时对象的创建和销毁。

  5. 监控和分析GC日志:定期监控和分析GC日志可以帮助识别潜在的内存泄漏、性能瓶颈和优化机会。可以使用JVM自带的工具(如jstat、jvisualvm)或第三方工具(如GCViewer、YourKit)来进行日志分析和性能调优。

八、G1核心配置参数

在JDK9及以后的版本中,G1是默认的垃圾收集器,但在JDK8中,你需要显式地启用。以下是G1收集器的一些核心配置参数:

  1. -XX:+UseG1GC:
    这个参数用于启用G1垃圾收集器。在JDK8中,你需要明确设置这个参数来使用G1,而在JDK9及更高版本中,G1是默认启用的。

  2. -XX:G1HeapRegionSize:
    这个参数用于设置每个Region的大小。Region是G1收集器管理内存的基本单位。该值必须是2的幂,范围在1MB到32MB之间。G1的目标是根据最小的Java堆大小划分出约2048个这样的区域。默认情况下,这个值是堆内存的1/2000,这意味着G1收集器管理的最小堆内存应该是2GB以上,最大堆内存为64GB。

  3. -XX:MaxGCPauseMillis:
    这个参数用于设置期望的最大GC停顿时间指标。G1收集器会尽力在这个时间内完成垃圾回收,以减少应用程序的停顿时间。默认值是200毫秒。

  4. -XX:ParallelGCThreads:
    这个参数用于设置并行垃圾回收的线程数。这个值通常设置为与可用的CPU核心数相等,最大可以设置为8。

  5. -XX:ConcGCThreads:
    这个参数用于设置并发标记的线程数。并发标记是G1收集器在垃圾回收过程中的一个阶段,这个阶段与应用程序线程并发执行。通常,这个值设置为并行垃圾回收线程数(ParallelGCThreads)的1/4左右。

  6. -XX:InitiatingHeapOccupancyPercent:
    这个参数用于设置触发并发GC周期的Java堆占用率阈值。当堆内存的占用率达到这个值时,G1收集器会启动一个并发GC周期。默认值是45%,这意味着当堆内存的45%被占用时,就会触发垃圾回收。

  7. -XX:+PrintGCDetails-verbose:gc:
    这两个参数不是G1特有的,但它们对于调试和监控垃圾收集器的行为非常有用。-XX:+PrintGCDetails会打印详细的垃圾收集日志,包括每次垃圾收集的时间、回收的对象数量等信息。-verbose:gc则会启用垃圾收集的日志记录,通常与-XX:+PrintGCDetails一起使用以获取更全面的日志输出。

在这里插入图片描述

九、结语

G1垃圾收集器以其可预测的停顿时间、灵活的内存管理和高效的并发标记等特点,在JVM中占据了重要的地位。通过深入理解G1的工作原理和关键特性,并根据实际应用场景进行性能优化,我们可以更好地利用G1来提升Java应用程序的性能和响应时间。



听说...关注下面公众号的人都变牛了,纯技术,纯干货 !

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1602540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【kubeEdge】离线部署

部署 kubeEdge 在线部署 在线方式部署直接执行以下命令,会联网下载需要的安装文件执行安装 $ keadm init --advertise-address{ip} --kubeedge-version{version} --kube-config{config_path}, 离线部署 离线准备工作 所有下载操作均在可以联网的机…

2024年在Vim中开发vue2+java

neovim 0.5刚出来的时代,那时刚有lua插件我很狂热。每天沉迷于打造自己的IDE之中。写过一堆相关的博客,也录过一些视频教程。后来发现neovim的接口和插件更新的很快,导致配置文件要不定期的修改,才能保证新版本的插件的适配。我也…

近端安全互联样例使用指导

样例介绍 本样例基于rk3568开发板,通过封装openharmony安全子系统deviceauth组件提供的能力,实现了一组可用于设备间快速建立可信认证和连接的接口,通过预先定义关系网,在设备初始化阶段完成端端设备间的认证,构建安全…

探索设计模式的魅力:融合AI大模型与函数式编程、开启智能编程新纪元

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》 💪🏻 制定明确可量化的目标,坚持默默的做事。 ✨欢迎加入探索AI大模型与函数式编程模式融合之旅✨ 在编程世界的广阔疆域里,两大…

mamba->jamba

1 mamba解决什么问题 Transformer的问题,其计算复杂度与序列长度的平方成正比,导致在处理长序列时效率低下。 Transformer 的注意力创建一个矩阵,将每个 token 与之前的每个 token 进行比较。矩阵中的权重由 token 对之间的相关性决定。 长度…

B树(B-tree)

B树(B-tree) B树(B-tree)是一种自平衡的多路查找树,主要用于磁盘或其他直接存取的辅助存储设备 B树能够保持数据有序,并允许在对数时间内完成查找、插入及删除等操作 这种数据结构常被应用在数据库和文件系统的实现上 B树的特点包括: B树为…

EelasticSearch是什么?及EelasticSearch的安装

一、概述 Elasticsearch 是一个基于 Apache Lucene 构建的开源分布式搜索引擎和分析引擎。它专为云计算环境设计,提供了一个分布式的、高可用的实时分析和搜索平台。Elasticsearch 可以处理大量数据,并且具备横向扩展能力,能够通过增加更多的…

如何获取手机root权限?

获取手机的 root 权限通常是指在 Android 设备上获取超级用户权限,这样用户就可以访问和修改系统文件、安装定制的 ROM、管理应用权限等。然而,需要注意的是,获取 root 权限可能会导致手机失去保修、安全性降低以及使系统变得不稳定。在获取 …

大话设计模式之单例模式

单例模式是一种创建型设计模式,它确保类只有一个实例,并提供一个全局访问点来访问该实例。 单例模式通常在以下情况下使用: 当一个类只能有一个实例,并且客户端需要访问该实例时。当该唯一实例需要被公开访问,以便在…

基于8B/10BGT收发器的PHY层设计(1)

一、PHY层简介 PHY层(Physical Layer)是OSI模型中最低的一层,也是最基本的一层,PHY是物理接口收发器,它实现物理层。包括MII/GMII(介质独立接口)子层、PCS(物理编码子层&#xff09…

c++的学习之路:24、 二叉搜索树概念

摘要 本章主要是讲一下二叉搜索树的实现 目录 摘要 一、二叉搜索树概念 二、 二叉搜索树操作 1、二叉搜索树的查找 2、二叉搜索树的插入 3、二叉搜索树的删除 三、二叉搜索树的实现 1、插入 2、中序遍历 3、删除 4、查找 四、二叉搜索树的递归实现 1、插入 2、删…

Java的maven项目导入本地jar包的三种方式

一、使用本地jar包 在项目中创建一个lib文件夹&#xff0c;将想要使用的本地jar包放进去 然后直接在pom.xml中添加下列依赖&#xff08;项目协作推荐&#xff09; <dependency><groupId>com.fpl</groupId><artifactId>spring</artifactId><…

牛客NC197 跳跃游戏(一)【中等 动态规划 Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/23407eccb76447038d7c0f568370c1bd 思路 答案说的merge区间就是每个A[i]的地方能跳到的最远坐标是A[i] [i]&#xff0c; 有一个maxReach&#xff0c;遍历一遍A[i], 不断刷新MaxReach, 如果某个i 位置比maxReac…

你觉得职场能力重要还是情商重要?

职场能力和情商都是职业成功的关键因素&#xff0c;它们在不同的情境和角色中扮演着不同的作用。很难简单地说哪一个更重要&#xff0c;因为它们通常是相辅相成的。 职场能力包括专业技能、知识水平、解决问题的能力、工作效率、创新思维等。这些能力是完成工作任务、达成职业目…

通讯录的实现(顺序表)

前言&#xff1a;上篇文章我们讲解的顺序表以及顺序表的具体实现过程&#xff0c;那么我们的顺序表在实际应用中又有什么作用呢&#xff1f;今天我们就基于顺序表来实现一下通讯录。 目录 一.准备工作 二.通讯录的实现 1.通讯录的初始化 2.插入联系人 3.删除联系人 4.…

一篇文章详细介绍Stable Diffusion模型原理及实现过程(附常用模型网站、下载方式)

目录 前言 何为Stable Diffusion模型&#xff1f; Stable Diffusion工作原理&#xff1a; Stable Diffusion模型的应用场景 Stable Diffusion免费使用网站 stability.ai: 本地部署 Stable Diffusion方法&#xff1a; StableDiffusion中文网 博主介绍&#xff1a;✌专注于前后端…

任务管理与守护进程

1.前台进程与后台进程 1.1守护进程 在上一章中&#xff0c;我们实现了一个Tcp服务器&#xff0c;但是这个服务器还存在一些问题&#xff0c;例如&#xff0c;我们将云服务器&#xff08;xshell&#xff09;关闭之后&#xff0c;服务器就无法使用了。 但是真正的服务器肯定不…

Stable Diffusion WebUI 控制网络 ControlNet 插件实现精准控图-详细教程

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本文主要介绍 Stable Diffusion WebUI 一个比较重要的插件 ControlNet&#xff08;控制网络&#xff09;&#xff0c;主…

第46篇:随机存取存储器(RAM)模块<五>

Q&#xff1a;本期我们使用Quartus软件的IP Catalog工具创建双端口RAM。 A&#xff1a;前期创建的RAM存储模块只有一个端口&#xff0c;同时为读/写操作提供地址。我们将再创建一个具有两个地址输入端口的RAM模块&#xff0c;分别为读操作和写操作提供地址。选择Basic Functio…

Ubuntu:VSCode中编译运行C++代码

版本&#xff1a;Ubuntu22.04.1 LTS 目录 1 安装VSCode并汉化 2 检查Ubuntu是否已经安装了 GCC 3 在VScode中安装C/C扩展 4 在VSCode中进行C/C配置 1 安装VSCode并汉化 安装VSCode&#xff08;参考之前博客Ubuntu&#xff1a;安装VSCode_ubuntu vscode-CSDN博客&#xff…