G1垃圾收集器详解

G1收集器

G1(Garbage-Frist)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以及高概率满足GC停顿时间要求的同时,还具备高吞吐量性能的特性.

在这里插入图片描述
在这里插入图片描述

G1将Java堆划分为多个大小相等的独立区域(Region),JVM目标似乎不超过2048个Region(JVM源码里TARGET_REGION_NUMBER定义),实际可以超过该值,但是不推荐.

一般Region大小等于堆除以2048.例如堆大小为4096M,则Region大小为2M,当然也可以用参数-XX:G1HeapRegionSize手动指定Region大小,但是推荐默认的计算方式.

G1保留了年轻代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合.

默认年轻代对堆内存的占比是5%,如果堆大小为4096M,那么年轻代占据200M左右的内存,对应大概是100个Region.可以通过-XX:G1NewSizePercent设置新生代初始占比,在系统运行中,JVM会不停的给年轻代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过-XX:G1MaxNewSizePercent调整.年轻代中的Eden和Survivor对应的Region也跟之前一样,默认8:1:1,假设年轻代现有100个Region,Eden区对应80个,S0对应10个,S1对应10个.

一个Region可能之前是年轻代,如果Region进行了垃圾回收,之后可能又会变成老年代,也就是Region的区域功能可能会动态变化.

G1垃圾收集器对于对象什么时候转移到老年代跟CMS是一样的,唯一不同的是对大对象的处理,G1有专门分配大对象的Region叫 Humongous区,而不是让大对象直接进入老年代的Region中.在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,比如按照上面算的,每个Region是2M,只要一个大对象超过了1M,就会被放入Humongous中,而且一个大对象如果太大,可能会横跨多个Region来存放.

Humongous区专门存放短期巨型对象,不用直接进老年代,可以节约老年代的空间,避免因为老年代空间不够的GC开销.

Full GC的时候除了收集年轻代和老年代之外,也会将Humongous区一并回收.

G1收集器一次GC(主要指Mixed GC)的运作过程大致分为以下几个步骤:

  • 初始标记(initial mark,STW):暂停所有的其他线程,并记录下GC Roots直接能引用的对象,速度很快.
  • 并发标记(Concurrent Marking):并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行.因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变.
  • 最终标记(Remark,STW):最终标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记变动的那一部分对象的标注记录(主要是处理漏标问题),这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短.
  • 筛选回收(Cleanup,STW):筛选回收阶段首先对各个Region的 回收价值和成本进行排序,根据用户所期望的GC停顿STW时间(可以用参数-XX:MaxGCPauseMillis指定)来制定回收计划,比如说老年代此时有1000个Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿200ms,那么通过之前回收成本计算得知,可能回收800个Region刚好需要200ms,那么就只会回收800个Region(Collection Set,要回收的集合),尽量把GC导致的停顿时间控制在我们指定的范围内.这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率.不管是年轻代或是老年代,回收算法主要用的是复制算法,将一个Region中存活的对象复制到另一个Region中,这种不会像CMS那样回收完成会有很多内存碎片需要整理一次,G1采用的复制算法回收几乎不会有太多的内存碎片.(注意:CMS回收阶段是跟用户线程一起并发执行的.G1因为内部实现太复杂暂时没实现并发回收).

在这里插入图片描述

G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字的又来Garbage-First的由来),比如一个Region花200ms能回收10M的垃圾,另外一个Region花50ms就能回收20M的垃圾,在回收时间有限的情况下,G1当然会优先选择后面这个Region回收.这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以尽可能高的收集效率.

被视为JDK1.7以上版本Java虚拟机的一个重要进化特征.它具备以下特点:

  • 并行与并发:G1能充分理由CPU,多核环境下的硬件优势,使用多个CPU(CPU或CPU核心)来缩短STW停顿时间.部分其他收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行.
  • 分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个堆,但是还是保留的分代的概念.
  • 空间整合:与CMS的"标记-整理"算法不同,G1从整体来看是基于"标记-整理"算法实现的收集器;从局部来看是基于"标记-复制"算法实现的.
  • 可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立 可预测的停顿时间模型,能够让使用者明确指定在一个长度为M毫秒的时间内(通过参数-XX:MaxGCPauseMillis指定)内完成垃圾收集

毫无疑问,可以由用户指定期望的停顿时间是G1收集器很强大的一个功能,设置不同的期望停顿时间,可使得G1在不同应用场景中取得关注吞吐量和关注延迟之间的一个最佳平衡.不过,这里设置的"期望值"必须是符合实际的,不能异想天开,毕竟G1是要冻结用户线程来复制对象的,这个停顿时间再怎么低也得有个限度.它默认的停顿时间是200ms,一般来说,回收阶段占到几十甚至近200ms都很正常,但如果我们把停顿时间调的特别低,比如设置为20ms,很可能出现的结果就是由于停顿目标时间太短,导致每次选出来的回收集只占堆内存很小的一部分,收集器收集的速度逐渐跟不上分配器分配的速度,导致垃圾慢慢堆积.很可能一开始收集器还能从空闲的堆内存中获得一些喘息的时间,但应用运行时间一长就不行了,最终占满堆引发Full GC反而降低性能,所以通常把期望停顿时间设置为一两百毫秒或者两三百毫秒都是比较合理的.

G1垃圾收集分类

  • Young GC:Young GC并不是说现有的Eden区放慢了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数-XX:MaxGCPauseMillis设定的值,那么增加年轻代的Region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时间接近参数-XX:MaxGCPauseMillis设定的值,那么就会触发Young GC
  • Mixed GC:不是Full GC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定Old区垃圾收集的优先顺序)以及 大对象区,正常情况G1的垃圾收集是先做Mixed GC,主要使用复制算法,需要把各个Region中存活的对象拷贝到别的Region里去,拷贝过程中如果发现 没有足够的空的Region 能够承载拷贝对象就会触发一次Full GC
  • Full GC:停止系统程序,然后采用单线程进行标记,清理和压缩整理,好空闲出一批Region来供下一次Mixed GC使用,这个过程是非常耗时的.

G1收集器参数设置

-XX:+UseG1GC:使用G1收集器

-XX:ParallelGCThreads:指定GC工作的线程数量

-XX:G1HeapRegionSize:指定分区大小(1M-32M,且必须是2的N次幂),默认将整堆划分为2048个分区

-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)

-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%,值配置整数,默认就是百分比)

-XX:G1MaxNewSizePercent:新生代内存最大空间

-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+2+年轻n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代.

-XX:MaxTenuringThreshold:最大年龄阈值(默认15)

-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则新生代和老年代的混合收集(Mixed GC),比如堆默认2048个Region,如果有接近1000个Region都是老年代的Region,则可能就要触发Mixed GC了

-XX:G1MixedGCLiveThresholdPercent:Region中存活对象低于这个值才会回收该Region(默认85%),如果超过这个值,存活对象过多,回收的意义不大

-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会儿,然后暂停回收,恢复系统运行,一会儿再开始回收,这样可以让系统不至于单次停顿时间过长.

-XX:G1HeapWastePercent:GC过程中空出来的Region是否充足阈值(默认5%),在混合回收的时候,对于Region回收都是基于复制算法进行的,都是要把回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话,回收过程中就会不断空出来新的Region,一旦空闲出来的Region达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就要结束了.

G1垃圾收集器优化建议

假设参数-XX:MaxGCPauseMills设置的值很大,导致系统运行很久才会做年轻代GC,年轻代可能都占用堆内存的60%了,此时才触发年轻代GC,那么存活下来的对象可能就会很多,此时就会导致Survivor区放不下那么多的对象就会进入老年代中,或者年轻代的GC过后,存活下来的对象过多,导致进入Survivor区域触发了动态年龄判定规则,达到了Survivor区域的50%,也会快速导致一些对象进入老年代中.

假设参数-XX:MaxGCPauseMills设置的值很小,随着系统的运行.最终占满堆引发Full GC反而降低性能.

G1垃圾收集器优化主要是要根据场景设计一个合理的-XX:MaxGCPauseMills.

G1适用场景

  1. 50%以上的堆被存活对象占用
  2. 对象分配和晋升的速度变化非常大
  3. 垃圾回收时间特别长,超过1s
  4. 8G以上的堆内存(建议值)
  5. 停顿时间是500ms以内

每秒几十W并发的系统如何优化JVM

Kafka类似的支撑高并发消息系统大家并不陌生,对于Kafka来说,每秒处理几万甚至几十万的消息是很正常的,一般来说部署Kafka需要用大内存机器(比如64G),也就是说可以给年轻代分配三四十G的内存来支持高并发处理.这里就涉及到一个问题了,我们常说对于Eden区的Young GC是很快的,这种情况下它的内存还会很快么?很显然,不可能,因为内存太大,处理还是要花不少时间的,假设三四十G内存回收可能最快也要几秒钟,按Kafka这个并发量放满三四十G的Eden区可能也就一两分钟吧,那么就意味着整个系统每运行一两分钟就会因为Young GC卡顿几秒钟没法处理新消息,显然是不行的.那么对于这种情况如何优化?我们可以适用G1收集器,设置-XX:MaxGCPauseMills为50ms,假设50ms能够回收三四G内存,然后50ms的卡顿其实完全能够接受,用户几乎无感知,那么整个系统就可以在卡顿几乎无感知的情况下一遍处理业务一遍收集垃圾.

G1天生就适合这中大内存机器的JVM运行,可以比较完美的解决大内存垃圾回收时间过长的问题.

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

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

相关文章

STM32自己从零开始实操01:原理图

在听完老师关于 STM32 物联网项目的所有硬件课程之后,就是感觉自己云里雾里,明明课程都认真听完了,笔记也认真记录,但是就是感觉学到的知识还不是自己。 遂决定站在老师的肩膀上自己开始设计项目,将知识变成自己的&am…

WSL2无法ping通本地主机ip的解决办法

刚装完WSL2的Ubuntu子系统时,可能无法ping通本地主机的ip: WSL2系统ip: 本地主机ip: 在powershell里输入如下的命令: New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias &quo…

Java中单例设计模式详解

Java中单例设计模式详解 在Java编程中,单例设计模式是一种创建对象的设计模式,它保证一个类仅有一个实例,并提供一个全局访问点来访问这个唯一实例。单例模式在很多场景下都非常有用,比如配置文件读取、线程池管理、缓存管理等。…

http基础了解

超文本传输协议(HTTP)是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的。HTTP 遵循经典的客户端—服务端模型,客户端打开一个连…

Nginx入门-01

必备知识 DNS域名解析 hostName主机名称其实就是我们的域名 www.baidu.com是一个域名 www.taobao.com也是一个域名 localhost也是一个域名 我们可以根据域名dns解析成ip地址 域名的存在就是为了方便我们的记忆,最终访问的时候还是要转换成服务器的IP地址进行…

Python编程----递归求解兔子的数量

描述 兔子的数量以这样的方式增长:每个月的兔子数量等于它前一个月的兔子数量加它前两个月的兔子数量,即f(n)f(n-1)f(n-2)。假设第1个月的兔子有2只,第2个月的兔子有3只,你能使用递归的方法求得第n个月的兔子有多少只吗&#xff…

Tomcat架构设计精髓分析-Connector高内聚低耦合设计

优秀的模块化设计通常都会采用高内聚、低耦合 高内聚是指相关度比较高的功能要尽可能集中,不要分散。低耦合是指两个相关的模块要尽可能减少依赖的部分和降低依赖的程序,不要让两个模块产中强依赖。 Tomca连接器需要实现的功能: 监听网络端口 接受网络…

STM32的GPIO输入和输出函数详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. GPIO模式 2. GPIO输出 2.1 RCC 2.2 GPIO 3. 代码示例 3.1 RCC时钟 3.2 GPIO初始化 3.3 GPIO输出函数 3.4 推挽输出和开漏输出 4. GPIO输入 4.1 输入模式 4.2 数据读取函数 5. C语言语法 1…

【Linux系统编程】第八弹---权限管理操作(中)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、修改文件权限的做法(二) 2、文件类型 3、可执行权限 4、创建文件/目录的默认权限 4.1、权限掩码 总结 前面一弹我们学…

pip 安装for mac

Pip3 配置 配置安装pypi 三方库 MisdeMacBook-Pro:~ mis$ mkdir .pip MisdeMacBook-Pro:~ mis$ cd .pip MisdeMacBook-Pro:.pip mis$ touch pip.conf MisdeMacBook-Pro:.pip mis$ ls pip.conf MisdeMacBook-Pro:.pip mis$ vim pip.conf 进入输入i,再输入如下&#xff1…

SpringCloudAlibaba之Sentinel简单使用

SpringCloudAlibaba之Sentinel简单使用 文章目录 SpringCloudAlibaba之Sentinel简单使用sentinel入门资源定义SphU(抛出异常方式)SphO(布尔类型方式)SentinelResource(注解的方式定义)SentinelResource使用前置条件使用SentinelResource定义资源定义blockHandler和fallback方法…

std::string的赋值

文章目录 std::string的赋值概述笔记END std::string的赋值 概述 看网上的例子,给std::string赋值,大部分都是直接赋值, 当成一个字符串来用。 如果赋值给std::string的是一段buffer(不可见字符),那网上的例子就不合适了。 实验了一下&…

PostgreSQL的扩展(extensions)-常用的扩展之pg_repack

PostgreSQL的扩展(extensions)-常用的扩展之pg_repack pg_repack 是一款非常有用的 PostgreSQL 扩展工具,它能够重新打包(repack)表和索引以回收空间并减少碎片,而且在这个过程中不会锁定表,允…

基于SpringBoot + Vue实现的校园(通知、投票)管理系统设计与实现+毕业论文(12000字)+答辩PPT+指导搭建视频

目录 项目介绍 运行环境 技术栈 效果展示 论文展示 总结 项目介绍 本系统包含管理员、用户、院校管理员三个角色。 管理员角色:用户管理、院校管理、单位类别管理、院校管理员管理、单位管理、通知推送管理、投票信息管理、通知回复管理等。 用户角色&#…

24.Feign性能优化

feign底层客户端实现: URLConnection:默认实现,不支持连接池,性能不太好。--feign的默认实现。 Apache HttpClient: 支持连接池。 OKHttp: 支持连接池。 优化办法: 1.用连接池替代默认的URLConnection。 2.日志级别&#xf…

golang面试题:怎么避免内存逃逸?

问题 怎么避免内存逃逸? 怎么答 在runtime/stubs.go:133有个函数叫noescape。noescape可以在逃逸分析中隐藏一个指针。让这个指针在逃逸分析中不会被检测为逃逸。 // noescape hides a pointer from escape analysis. noescape is// the identity function but …

深入OceanBase内部机制:分区机制构建高可用、高性能的分布式数据库基石

码到三十五 : 个人主页 在数据库技术的发展历程中,随着数据量的不断增长和业务需求的日益复杂,如何高效地存储、查询和处理数据成为了关键挑战。OceanBase作为一款高性能、高可用的分布式关系数据库,通过其独特的分区机制&#xf…

Unity对应的c#版本

本文主要是记录一下unity已经开始兼容c#的版本和.net版本,以便更好的利用c#的特性。 c#和.net对应情况 微软已经将.net开发到.net 9了,但是unity的迭代速度远没有c#迭代速度快,已知unity最新的LTS版本unity2023已经兼容了c#9 可以在unity手册…

8K+/月!学习就业一把梭,祝贺誉天数通陈同学收获心仪offer !

大家好,我是誉天数通的陈同学。 在武汉,网络运维圈的人应该都会听过誉天的名字。作为华为的授权培训合作伙伴,誉天不仅提供专业全面的工程师培训,还以其独特的环境和氛围吸引着无数有志于在网络领域发展的朋友。 一年前的我&#…

JAVA学习-行为抽象和Lambda.分区

行为抽象和Lambda分区是Java编程中的重要概念,它们可以帮助我们更好地组织和处理数据。下面我们来详细了解一下这两个概念。 一、行为抽象 行为抽象是指将某个行为抽象出来,使得不同的对象可以共享这个行为。在Java中,我们可以使用接口或者抽…