解读Java虚拟机垃圾回收器:探究经典算法背后的奥秘

目录

一、GC分类与性能指标

(一)垃圾回收器分类

(二)性能指标

(三)不可能三角

二、不同的垃圾回收器概述

三、Serial回收器:串行回收

四、ParNew回收器:并行回收

五、Parallel回收器:吞吐量优先

六、CMS回收器:低延迟

七、G1回收器:区域化分代式

(一)Region

(二)记忆集

(三)运行过程

八、垃圾回收器总结

九、GC日志分析


一、GC分类与性能指标

(一)垃圾回收器分类

1、按垃圾回收线程数

串行回收指同一个时间段内,只允许一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直到垃圾收集工作结束。在单CPU处理器或者较小应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器。所以串行回收默认被应用在客户端的client模式下的JVM中

并行垃圾回收器,和串行相反,并行收集可以运用在多个CPU同时执行垃圾回收,因此提升了应用的吞吐量,不过并行回收仍然与串行回收一样,采用独占式,使用了STW机制

2、按照工作模式分

并发式:垃圾回收器与应用程序交替工作,以尽可能减少应用程序的停顿时间

独占式:一旦运行,就停止应用程序中所有的用户线程,直到垃圾回收过程完全结束

3、按照碎片处理方式

压缩式,比如标记整理算法,内存分配可以使用指针碰撞。

非压缩式,比如标记清除算法,会产生垃圾碎片,内存分配使用空闲列表

4、按个工作内存区间分

年轻代

老年代

(二)性能指标

1、吞吐量

运行用户代码的时间占总运行时间的比例

总运行时间:程序的运行时间+内存回收的时间

吞吐量优先,意味着单位时间内,STW的时间最短

2、垃圾收集开销

吞吐量的补数(运行时间=运行用户代码的时间+垃圾回收的时间),垃圾收集所占用的时间与总运行时间的比例

3、暂停时间

执行垃圾收集时,程序的工作线程被暂停的时间

暂停时间优先,意味着单次STW的时间最短,但是频率可能增加

4、收集频率

相对于应用程序的执行,收集操作发生的频率

5、内存占用

Java堆区所占的内存大小

6、快速

一个对象从诞生到被回收经历的时间

(三)不可能三角

简单来说抓住两点,吞吐量暂停时间

高吞吐量与低暂停时间,是一对互相竞争的。因为如果高吞吐量优先,必然需要降低内存回收的执行频率,导致GC需要更长的暂停时间来执行内存回收。

如果选择低暂停时间(低延迟)优先为原则,也只能频繁的执行内存回收,引起程序吞吐量的下降

现在的标准,在最大吞吐量优先的情况下,降低停顿时间(STW)

二、不同的垃圾回收器概述

7款经典垃圾收集器和垃圾分代之间的关系

垃圾收集器的组合关系

  • jdk8之前,可以用虚线参考关系
  • CMS下面的实线,是CMS回收失败的后备方案
  • JDK8中取消了红线的组合,标记为废弃的。如果要用也可以用。
  • JDK9中将红线做了remove
  • jdk14中弃用了绿线组合
  • jdk14中删除了CMS GC
  • JDK9默认G1
  • JDK8默认Parallel Scavenge 和Parallel old Gc
  • 新生代用了Parallel Scavenge 则老年代自动触发用Parallel old
  • Parallel底层与ParNew底层不同,所以不能和CMS组合

如何查看默认的垃圾收集器?

  1. -XX:+PrintCommandLineFlags
  2. jinfo -flag 相关垃圾回收器参数 进程ID

三、Serial回收器:串行回收

Serial收集器采用复制算法,串行回收和STW机制的方式执行内存回收。除了年轻代,还有用于执行老年代的Serial old收集器,同样采取了串行回收,但是用标记压缩算法。

使用一个CPU或者一条收集线程去完成垃圾收集工作,在进行垃圾收集时,必须暂停其他所有工作线程

优势:简单而高效,对于限定单个CPU的环境来说,由于没有线程交互的开销,可以获取最高的单线程收集效率

HotSpot虚拟机中,使用-XX:+UseSerialGC指定年轻代和老年代使用串行收集器

对于交互强的应用而言,并不会采取串行垃圾收集器

四、ParNew回收器:并行回收

除了采用并行回收,其他方面和Serial之间几乎没有任何区别

-XX:UseParNewGC 手动指定ParNew收集器执行内存回收任务,它表示年轻代使用,不影响老年代

-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数据相同的线程数

五、Parallel回收器:吞吐量优先

也采用并行回收,同样是基于标记-复制算法实现的,但和ParNew不同,它的目标是达到一个可控制的吞吐量。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值,

即:

Parallel Scavenge 收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间
-XXMaxGCPauseMillis 参数以及直接设置吞吐量大小 -XXGCTimeRatio 参数。

接下来介绍参数

  • -XX:+UseParallelGC
    • 手动指定年轻代使用此收集器执行内存回收任务
  • -XX:+UseParallelOldGC
    • 手工指定老年代使用并行回收收集器,分别适用于新生代和老年代,默认jdk8是开启的
    • 上面这两个参数相互关联,开启一个,默认开启另一个。
  • -XX:ParallelGCThreads
    • 设置年轻代并行收集器的线程数,一般与CPU数量相同,如果CPU数量大于8个,则值=3+(5*N/8)
  • -XX:MaxGCPauseMillis
  • 设置收集器最大停顿时间,单位毫秒。允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过用户设定值。
  • -XX:GCTimeRatio
    • 垃圾收集占总时间比,用于衡量吞吐量大小
    • 默认99,取值范围0-100,也就是垃圾回收时间不超过1%
    • 与上一个参数矛盾,暂停时间越长,Ratio参数就容易超过设定比例
  • -XX:+UseAdaptiveSizePolicy
    • 开启自适应调节策略。这种模式下,年轻代大小,Eden和Survivor的比例,晋升老年底对象年龄参数都会被自动调整
    • 为了达到堆大小,吞吐量和停顿时间之间的平衡点
    • 在手动调优比较困难的场景下,可以直接用自适应方式,仅指定虚拟机最大堆,目标吞吐量和停顿时间,让虚拟机自己完成调优工作

六、CMS回收器:低延迟

jdk1.5推出 Concurrent Mark Sweep 并发的标记清除,第一次实现了让垃圾收集线程与用户线程同时工作

从名字(包含 “Mark Sweep” )上就可以看出 CMS 收集器是基于标记 - 清除算法实现的,它的运作
过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:
  1. 初始标记:STW,仅仅只是标记处GC Roots能直接关联的对象,一旦标记完成后就会恢复之前被暂停的所有应用线程,由于直接关联对象比较小,所以这里速度非常快
  2. 并发标记:从GCRoots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要停顿用户线程。可以与垃圾收集线程一起并发运行
  3. 重新标记:为了修正并发标记期间,因用户程序继续运作导致标记产生变动的那一部分对象的标记记录
  4. 并发清除:清理删除标记阶段判断的已经死亡的对象,释放内存空间。由于不需要移动存活对象,所以这个阶段也可以与用户线程同时并发

初始标记和重新标记阶段仍然需要STW机制

而且由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此CMS收集器不能像其他收集器那样等到老年代几乎填满再进行回收,而是当堆内存使用率达到某一阈值时,便开始进行回收。

要是CMS运行期间预留的内存无法满足程序需要,就会出现一次Concurrent Mode Failure失败,这时虚拟机启用备用方案,临时启用Serial old 收集器来重新进行老年代的垃圾收集,这样停顿时间就长了。

CMS采取标记清除算法,会产生内存碎片,只能够选择空闲列表执行内存分配

为什么不采取标记压缩呢?

因为并发清除时,如果用压缩整理内存,原来的用户线程使用的内存就无法使用了。标记压缩更适合STW场景下使用

优点

  • 并发收集
  • 低延迟

缺点

  • 会产生内存碎片
  • 对CPU资源非常敏感
    • 在并发阶段会占用一部分线程导致应用程序变慢
  • 无法处理浮动垃圾
    • 并发标记阶段是与工作线程同时运行,如果并发阶段产生垃圾对象,CMS无法进行标记,导致新产生的垃圾对象没有被及时回收,只能在下一次执行GC时释放空间

接下来介绍参数

  • -XX:+UseConcMarkSweepGC
    • 手工指定CMS收集器执行内存回收任务
    • 开启后,自动将-XX:UseParNewGC打开,即ParNew(Young区)+CMS(old区)+Serial GC组合
  • -XX:CMSlnitiatingOccupanyFraction
    • 设置堆内存使用率的阈值
    • 一旦达到该阈值,则开始进行回收
    • jdk5及之前默认68,即老年代的空间使用率达到68%时会执行一次CMS回收
    • JDK6及以上默认值为92%
    • 如果内存增长缓慢,可以设置一个稍大的值,有效降低CMS的触发频率,减少老年代回收的次数
    • 如果应用程序内存使用率增加很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。
  • -XX:+UseCMSCompactAtFullCollection
    • 用于执行完Full GC后对内存空间进行压缩整理
    • 不过内存压缩无法并发执行,会带来停顿时间更长的问题
  • -XX:CMSFullGCsBeforeCompaction
    • 设置执行多少次FullGC后对内存空间进行压缩整理
  • -XX:ParallelCMSThreads
    • 设置CMS的线程数量
    • 默认启动的线程数是(ParallelGCThreads+3)/4
    • ParallelGCThreads是年轻代并行收集器的线程数

如果想要最小化使用内存和并行开销,选择Serial GC

如果最大化应用程序的吞吐量,选择ParallelGC

如果想要最小化的GC的中断或停顿时间,选择CMS GC

七、G1回收器:区域化分代式

官方给G1设定的目标就是在延迟可控的情况下,获得尽可能高的吞吐量,所以才担当起全功能收集器的重任和期望

在JDK1.7版本正式启用,jdk9以后默认垃圾回收器。JDK8还不是默认的,需要用-XX:+UseG1GC来启用

(一)Region

Garbage First是一个并行回收器,他把堆内存分割为很多不相关的区域(Region)(物理上不连续),使用不同的region表示Eden,s0,s1,老年代等。G1会跟踪各个region里面垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region

收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
Region 中还有一类特殊的 Humongous 区域,专门用来存储大对象。 G1 认为只要大小超过了一个
Region 容量一半的对象即可判定为大对象。每个 Region 的大小可以通过参数 -XX: G1HeapRegionSize 设定,取值范围为1MB 32MB ,且应为 2 N 次幂。而对于那些超过了整个 Region 容量的超级大对象,将会被存放在N 个连续的 Humongous Region 之中, G1 的大多数行为都把 Humongous Region 作为老年代的一部分来进行看待。

将Java堆分成多个独立Region后,Region里面存在的跨Region引用对象如何解决?
比如进行新生代垃圾回收时,很有可能有老年代的对象指向新生代中的对象,如果此时还需对老年代的对象进行一次扫描,代价太高昂了。G1收集器使用记忆集来解决这个问题

(二)记忆集

每个region对应一个记忆集,通过记忆集避免全局扫描。这些记忆集会记录下别的Region 指向自己的指针,并标记这些指针分别在哪些卡页的范围之内。

每次引用类型数据写操作时,会产生一个写屏障暂时中断操作。然后检查将要赋值的引用指向的对象是否和该引用对象类型数据在不同的region,如果不同就通过CardTable把相关的引用信息记录到引用指向对象所在的Region对应的记忆集中

当进行垃圾收集时,在GC根节点枚举范围加入记忆集,就可以保证不进行全局扫描,也不会有遗漏

(三)运行过程

如果我们不去计算用户线程运行过程中的动作(如使用写屏障维护记忆集的操作), G1 收集器的
运作过程大致可划分为以下四个步骤:
初始标记 Initial Marking ):仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改 TAMS 指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region 中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC 的时候同步完成的,所以 G1 收集器在这个阶段实际并没有额外的停顿。
并发标记 Concurrent Marking ):从 GC Root 开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。
最终标记 Final Marking ):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留
下来的最后那少量的 SATB 记录。
筛选回收 Live Data Counting and Evacuation ):负责更新 Region 的统计数据,对各个 Region 的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region 构成回收集,然后把决定回收的那一部分Region 的存活对象复制到空的 Region 中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的

优势

  • 并行与并发
  • 分代收集:同时兼顾年轻代与老年代
  • 空间整合
    • region之间用复制算法,整体可以看做是标记压缩算法。
    • 两种算法都避免内存碎片,有利于程序长时间运行,分配大对象不会因为无法找到连续空间提前触发下一次GC,尤其当Java堆非常大的时候,G1优势更加明显
  • 可预测的停顿时间模型
    • 能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不能超过N毫秒

缺点

  • 相较于CMS,G1不具备全方位,压倒性优势。比如用户程序运行中,G1无论是为了垃圾收集产生的内存占用,还是程序运行时的额外执行负载都要比CMS要高
  • 经验上来说,小内存应用CMS表现大概率优于G1,在大内存上G1优势发挥更多,平衡点再6-8GB

参数设置

  • -XX:+UseG1GC
  • -XX:G1HeapRegionSize
    • 设置每个Region大小,值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆划分出约2048个区域,默认是堆内存的1/2000
  • -XX:MaxGCPauseMillis
    • 设置期望达到的最大GC停顿时间指标,JVM尽力但不保证,默认200ms
  • -XX:ParallelGCThread
    • 设置STW工作线程数的值,最多设置8
  • -XX:ConcGCThreads
    • 设置并发标记的线程数,将N设置为并行垃圾回收线程数(parallelGCThreads)的1/4左右
  • -XX:InitiatingHeapOccupancyPercent
    • 设置触发并发GC周期的Java堆占用率阈值,超过此值就触发GC,默认是45

G1提供了三种垃圾回收模式在不同的条件下触发

  • YoungGC:当年轻代eden区用尽时
  • MixedGC:G1老年代回收器不需要整个老年代都被回收,一次只需要扫描回收价值高的小部分老年代的region就可以了。同时这个老年代回收是和年轻代一起被回收的。
  • FullGC:当堆内存使用到一定值,默认45%

八、垃圾回收器总结

九、GC日志分析

我们可以通过加以下参数输出GC日志,然后进行分析

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

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

相关文章

php使用CORS解决跨域

CORS 是一种由 W3C 制定的跨域资源共享标准&#xff0c;通过在服务器端设置 HTTP 头来实现跨域请求。 在 PHP 中&#xff0c;可以通过设置响应头来启用 CORS&#xff1a; <?php // 服务器端 PHP 文件 header(Access-Control-Allow-Origin: *); // 允许所有域名访问 heade…

Spring Boot 集成 spring security 01

一、导入依赖&#xff08;pom.xml&#xff09; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&qu…

态势感知是什么

在当今高度信息化的时代&#xff0c;信息安全风险已经成为企业、政府和个人的重要关注点。为了有效应对这些风险&#xff0c;态势感知成为了一种日益重要的能力。态势感知是一种基于环境的、动态、整体地洞悉安全风险的能力&#xff0c;是以安全大数据为基础&#xff0c;从全局…

软件产品经理常用的ChatGPT通用提示词模板

产品规划和发展战略&#xff1a;请帮助我制定软件产品的规划和发展战略&#xff0c;包括市场调研、用户需求分析、产品定位、竞争对手分析等方面的内容&#xff0c;以便我能够更好地把握市场机会并制定相应的发展策略。 产品设计和开发&#xff1a;请帮助我进行软件产品的设计…

听GPT 讲Rust源代码--src/tools(5)

File: rust/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs 在Rust源代码中&#xff0c;lower.rs文件位于Rust Analyzer项目的hir-ty子库中&#xff0c;其目的是将高级中间表示&#xff08;HIR&#xff09;降低为中间表示&#xff08;MIR&#xff09;。下面对文件及其…

Python----文件备份案例

实例代码 # 1、接收用户输入的文件名&#xff08;要备份的文件名&#xff09; oldname input(请输入要备份的文件名称&#xff1a;) # python.txt # 2、规划备份文件名&#xff08;python[备份].txt&#xff09; # 搜索点号 index oldname.rfind(.) # 返回文件名和文件后缀 …

数据结构-归并排序

归并排序 基本概念 归并是指将两个或两个以上的有序表合并成一个有序表。 基本思想 假设有N个记录&#xff0c;则可以看成是N个有序的子序列&#xff0c;每个子序列的长度为1&#xff0c;然后两两归并得到[n/2] 个&#xff08;上取整&#xff09;长度为2的子序列&#xff…

css实现最简单的3d透视效果,通过旋转可以直观感受到

css的3d效果还是非常复杂的&#xff0c;我今天简单学习了一下入门&#xff0c;实现了一个超级简单的效果&#xff0c;帮助我自己理解这个3d的过程&#xff0c;实现的效果动画如下&#xff1a;可以通过调整父元素旋转的角度&#xff0c;更加直观的感受这个3d效果&#xff1a; 实…

【开源视频联动物联网平台】j2mod库对指令码的定义

在J2Mod库中&#xff0c;Modbus通信使用指令码来标识要执行的操作。指令码&#xff08;Function Code&#xff09;是Modbus协议中用于定义请求和响应类型的数字代码。不同的指令码表示不同的功能&#xff0c;例如读取保持寄存器、写入单个寄存器等。以下是一些常见的Modbus指令…

C/S与B/S的区别

B/S与C/S理解 C/S结构B/S结构问题数据放在服务器端与客户端的利弊 C/S结构 客户端&#xff1a;用户安装的软件 服务端&#xff1a;统一管理数据库的主机中的软件 叫做服务端。 B/S结构 用户通过浏览器实现&#xff08;往往表示业务逻辑在前端进行实现&#xff0c;主要业务逻…

【数据结构初阶】双向链表

各位读者老爷好&#xff0c;很高兴你又来读本鼠鼠的博客。鼠鼠我呀基于C语言实现一下双向链表&#xff0c;有兴趣的读者老爷可以瞅瞅哈&#xff01; 目录 1.定义双向链表节点 2.初始化哨兵位 3.双向链表销毁 4.双向链表打印 5.双向链表在pos的前面进行插入 6.双向链表删除…

初学者如何入门深度学习:以手写数字字符识别为例看AI 的学习路径,一图胜千言!超多高清大图收集整理

文章大纲 深度神经网络机器学习,深度学习,数据发掘之间的关系理解深度神经网络最好的可视化工具深度学习基础概念能解决神马种类的问题?卷积池化以手写字符识别为例讲述深度学习的分类问题MNIST 数据集简介初学者入门 :生成式 AI -- generative-ai-for-beginners从神经网络…

Linux系统之部署Plik临时文件上传系统

Linux系统之部署Plik临时文件上传系统 一、Plik介绍1.1 Plik简介1.2 Plik特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、下载Plik软件包4.1 创建下载目录4.2 下载Plik软件包4.3 查看下载的Plik软件…

3.4_1 java自制小工具 - pdf批量转图片

相关链接 目录参考文章&#xff1a;pdf转图片(apache pdfbox)参考文章&#xff1a;GUI界面-awt参考文章&#xff1a;jar包转exe(exe4j)参考文章&#xff1a;IDEA导入GIT项目参考文章&#xff1a;IDEA中使用Gitee管理代码gitee项目链接&#xff1a;pdf_2_image网盘地址&#xf…

Mysql安全之基础合规配置

一、背景 某次某平台进行安全性符合型评估时&#xff0c;列出了数据库相关安全选项&#xff0c;本文特对此记录&#xff0c;以供备忘参考。 二、安全配置 2.1、数据库系统登录时的用户进行身份标识和鉴别&#xff1b; 1&#xff09;对登录Mysql系统用户的密码复杂度是否有要…

Nacos 客户端版本从1.x 升级到 2.x 的排坑记

问题描述 应用引入 Nacos Config 配置管理功能&#xff0c;应用启动时读取 Nacos 配置中心的配置作为启动参数&#xff0c;其中包括数据源信息 url 。 当 Nacos 正在进行 GC 操作、无法响应客户端请求时&#xff0c;应用端刚启动时发送的登录认证请求 http://IP:PORT/nacos/v…

Azure Machine Learning - 使用 Python 进行语义排名

在 Azure AI 搜索中&#xff0c;语义排名是查询端功能&#xff0c;它使用 Microsoft AI 对搜索结果重新评分&#xff0c;将具有更多语义相关性的结果移动到列表顶部。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&am…

单链表的模拟实现

单链表的模拟实现 一&#xff1a;单链表的概念&#xff1a;二&#xff1a;单链表中的方法&#xff1a;1&#xff1a;得到单链表的长度2&#xff1a;查找是否包含关键字key是否在单链表当中3&#xff1a;打印单链表中的数据&#xff1a;display&#xff08;&#xff09;3&#x…

WPF实现文字纵向排布的TabItem

文章目录 基本用法文字竖排显示 WPF布局 基本用法 WPF中的TabControl是一个容器控件&#xff0c;用于在单个窗体或页面中承载多个选项卡。每个选项卡可以包含不同的控件&#xff0c;用于显示不同的内容&#xff0c;其最简单的调用方法如下&#xff0c;只需在TabControl中无脑…

1.3 Linux文件系统

一、Linux文件系统结构 Linux下都是文件&#xff0c;所以没有Windows一样的盘&#xff0c;有的只有文件夹。 cd /    // 进入根目录 ls     // 查看根目录"/"下的文件及文件夹 /bin    &#xff1a;存储了很多系统命令&#xff0c; /usr/sbin 也存储了…