深入剖析JVM垃圾收集器

news/2024/11/29 4:32:10/

文章目录

      • 前言
      • 1、新生代垃圾收集器
        • 1.1、Serial
        • 1.2、ParNew
        • 1.3、Parallel Scavenge
      • 2、老年代垃圾收集器
        • 2.1、Serial Old
        • 2.2、Parallel Old
        • 2.3、CMS(Concurrent Mark Sweep)
      • 3、全堆垃圾收集器
        • 3.1、Garbage First(G1)

前言

参考资料:《深入理解Java虚拟机》

垃圾收集器是GC的实践者,目前常用经典的垃圾收集器有7款,如下图所示:
在这里插入图片描述
收集器之间的连线说明可以搭配使用,JDK9标识代表在该版本官方已经不支持配合使用了!

接下来逐一介绍这几款垃圾收集器。

1、新生代垃圾收集器

1.1、Serial

顾名思义,单线程的垃圾收集器;这里的单线程不仅仅说明它只使用一个GC线程进行垃圾收集,更重要的是它进行GC时,其他的工作线程必须暂停,直到GC线程工作结束,这种现象就是大名鼎鼎的STW(Stop The World),这种现象对很多应用来说是不可接受的!

Serial/Serial Old收集器运行示意图:
在这里插入图片描述

1.2、ParNew

ParNew收集器实质是Serial收集器的多线程并行版本

可以认为ParNew除了同时使用多线程进行GC外,其余行为和Serial收集器完全一致(控制参数、STW、回收策略等),ParNew/Serial Old运行示意图如下:
在这里插入图片描述
CMS出现巩固了ParNew的地位,因为新生代只有Serial和ParNew能与CMS配合使用。不过好景不长,更先进的G1全堆垃圾收集器干掉了这对苦命鸳鸯,从JDK9开始,官方希望G1彻底取代这对组合。

ParNew在单核CPU中比Serial效果更差,因为需要频繁的上下文切换;ParNew默认开启的GC线程数与CPU核数相同,可以使用-XX:ParallelGCThreads参数限制GC的线程数。

1.3、Parallel Scavenge

与前两款新生代收集器一样,Parallel Scavenge同样采用标记-复制算法;该收集器还支持多个GC线程并行(看起来和ParNew没啥区别),接下来我们探讨一下它到底有什么特别之处。

该收集器专注于吞吐量吞吐量 = CPU运行用户代码时间 / 运行用户代码时间 + 运行GC时间,高吞吐量可以最高效率利用CPU资源,尽快完成程序运算任务,主要适合在后台运算而不需要太多交互的分析任务。

该收集器通过两个参数精确控制吞吐量

  1. -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间(通过牺牲新生代空间和吞吐量实现)
  2. -XX:GCTimeRatio:直接设置吞吐量大小(默认值99,尽可能保证应用程序执行时间为收集器执行时间的99倍,也就是收集器的时间消耗不超过总运行时间的1%)

相比于ParNew,该收集器最重要的特性自适应调节策略,通过参数-XX:+UseAdaptiveSizePolicy控制,参数激活后,就不需要手动指定新生代、Eden和Survivor区比例、晋升老年代对象大小等细节参数了,JVM会根据当前系统的运行情况收集性能监控信息,动态调整这些参数提供最合适的停顿时间或最大的吞吐量。

2、老年代垃圾收集器

2.1、Serial Old

和Serial基本一致,也是单线程垃圾收集器,只不过Serial使用于新生代,采用复制算法;Serial使用于老年代,采用标记-整理算法,这里就不过多介绍了,运行原理如图:
在这里插入图片描述

2.2、Parallel Old

可以认为它是Parallel Scavenge收集器的老年代版本,这里也不做过多介绍了,大致是一样的,在注重吞吐量或CPU资源稀缺的场合,可以有效考虑使用Parallel Scavenge + Parallel Old组合,运行示意图如下:

在这里插入图片描述

2.3、CMS(Concurrent Mark Sweep)

以获取最短回收停顿时间为目标的收集器。

相比于两外两个收集器,CMS采用的是标记-清除算法,它的运行过程分为四个步骤,其中,初始标记重新标记需要STW:

  1. 初始标记:仅仅标记GC Roots能直接关联到的对象,速度很快
  2. 并发标记:从GC Roots直接关联对象开始,遍历整个对象图的过程,过程耗时较长但不需要停顿用户线程
  3. 重新标记:为了修正并发标记阶段,因用户线程继续运行而导致标记产生变动那部分对象的标记记录,停顿时间大于初始标记,小于并发标记
  4. 并发清除:清除标记阶段判断的已经死亡对象,该阶段可以与用户线程同时并行

在这里插入图片描述

CMS有三个明显的缺点:

  1. CMS对CPU资源十分敏感
    虽然CMS不会造成用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,降低总吞吐量
  2. CMS无法处理浮动垃圾
    有可能出现Con-current Mode Failure失败进而导致另一次STW的Full GC产生。在CMS并发标记和并发清理阶段,用户线程还是在运行,自然就会有新的垃圾对象产生,这部分对象还是在标记过程结束后产生的,CMS无法在本次GC过程中处理掉它们,只能等到下一次清除;所以CMS不能像其他收集器那样等到老年代几乎填满在收集,必须预留一部分空间以供并发收集时程序运作使用。可以通过参数-XX:CMSInitiatingOccu-pancyFraction控制CMS触发百分比,设置太高容易造成大量的并发失败产生,设置太低会导致频繁进行GC
  3. CMS采用标记-清除算法,所以会有内存碎片问题

3、全堆垃圾收集器

3.1、Garbage First(G1)

G1开创了面向局部收集设计思路和基于Region的内存布局形式。

G1把连续的堆内存划分为多个大小相等的独立区域(Region),每个Region都可以根据需要扮演成Eden、Survivor、老年代空间,针对不同角色的Region采用不同的策略去处理,获取很好的收集结果。

Region中有一类特殊的Humongous区域,专门用于存储大对象。
G1认为只要大小超过一个Region一半的对象皆为大对象(Region大小可以通过参数-XX:G1HeapRegionSize设定,范围为1~32MB)。对于超过了整个Region容量的超级大对象会被存放在N个连续的Humongous Region中,G1将其看作老年代的一部分。

G1每次GC的内存空间都是Region大小的整数倍,它会跟踪每个Region垃圾价值大小,价值即回收所获得的空间大小以及回收所需的时间经验值,然后在后台维护一个优先级列表,优先回收收益最大的那些Region。

在这里插入图片描述

G1收集器的运作过程大致可划分为以下四个步骤:

  1. 初始标记:仅仅标记GC Roots直接关联的对象,并且修改TAMS指针,让下个阶段用户线程并发运行时正确地在可用的Region中分配新对象,耗时很短。
  2. 并发标记:从GC Root开始对堆中对象进行可达性分析,找到要回收的对象,可与用户线程并发执行,耗时较长。
  3. 最终标记:用户线程短暂暂停,用于处理并发阶段结束后遗留下来的最后那少量的SATB记录。
  4. 筛选回收:对各个Region根据回收价值和成本进行排序,根据用户期望停顿时间制定回收计划,自由选择任意多个Region作为回收集,然后把决定回收的部分Region存活对象复制到空的Region中,再清理掉整个旧Region全部空间。这里涉及对象的移动必须是暂停用户线程,多条GC线程并行完成。

在这里插入图片描述


http://www.ppmy.cn/news/18983.html

相关文章

开发人员必备的 15 个备忘单

随着网络编程技术的快速发展,我们必须学习很多新东西。有些语言和框架非常复杂,您可能记不住所有的语法或方法。备忘单是易于访问的笔记。当有人在过去目睹任何有帮助或有价值的事情时,包括我自己,我们都会做笔记。但是&#xff0…

java枚举类2023028

一个类的对象是有限而且固定的,比如季节类, 它只有4个对象;再比如行星类,目前只有8个对象。这种实例有限而且固定的类,在Java里被称为枚举类。在早期代码中,可能会直接使用简单的静态常量来表示枚&#xff…

C语言之程序结构和常量

2.1.1 C语言的发展及标准 C语言:一种通用的、面向过程的计算机程序设计语言(第三代高级语言)1972年,为了移植与开发UNIX操作系统,丹尼斯里奇在贝尔电话实验室设计开发了C语言为了利于C语言的全面推广,许多…

SelectPdf for .NET 22.0 Crack

SelectPdf for .NET 是一个专业的 PDF 库,可用于创建、编写、编辑、处理和读取 PDF 文件,而无需在 .NET 应用程序中使用任何外部依赖项。使用此 .NET PDF 库,您可以实现丰富的功能,从头开始创建 PDF 文件或完全通过 C#/VB.NET 处理…

AtCoder Regular Contest 154 题解

A - Swap Digit 给2个长度均为n的十进制数&#xff0c;你可以任意次交换2个数相同位置的数字&#xff0c;要求使它们乘积最小 让其中一个数最小&#xff0c;另一个数最大。 #include<bits/stdc.h> using namespace std; #define For(i,n) for(int i1;i<n;i) #defi…

Kotlin中空安全操作符,异常处理和自定义异常,以及先决条件函数详解

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家 &#x1f449;点击跳转到教程 一、Kotlin的可空性 null 在java中我们司空见惯的空指针异常NullPointerException,带给了我们很多麻烦。 Kotlin作为更强…

大数据之HBase集群搭建

文章目录前言一、上传并解压HBase安装包二、修改HBase配置文件&#xff08;一&#xff09;hbase-env.sh&#xff08;二&#xff09;hbase-site.xml三、配置环境变量四、复制jar包到lib文件夹五、修改regionservers文件六、分发安装包和配置文件七、启动Hbase八、验证HBase是否启…

我的创作纪念日——“永远相信美好的事情即将发生”

作者&#xff1a;非妃是公主 专栏&#xff1a;《程序人生》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录序与CSDN的往事机缘收获憧憬碎碎念序 第一次写创作纪念日的文章&#xff01;哈哈哈哈&#xff0c;今…