【JVM Ⅶ】经典的垃圾回收器

垃圾回收初级总结篇

在整个GC的学习过程中,监控与调优是最重要的,也就是实践是最重要的!

1. GC分类和性能指标


1.1 JDK都更新些什么?

【语法层面】:lambda表达式、switch、自动拆装箱、enum、多多练习

【API】:Stream API、集合框架、String底层优化、日期事件

【底层优化】:GC的变化、JVM、引擎的变化、(看不到的变化)

线程数区分:可以分为串行垃圾回收器和并行垃圾回收器

并行串行回收器 :如果是单个CPU,更适合使用的是串行。

工作内存区分:年轻代垃圾回收器和老年代垃圾回收器

1.2 性能指标

吞吐量:运行用户代码的时间占总运行时间的比例

  • (总运行时间:程序的运行时间十内存回收的时间)

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

内存占用: Java堆区所占的内存大小

垃圾收集开销:吞吐量的补数,垃圾收集所用时间与总运行时间的比例。

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

快速:一个对象从诞生到被回收所经历的时间。

1.3 吞吐量 VS 暂停时间

保证最大吞吐量的前提下拿到最少的暂停时间。

  • 不幸的是“高吞吐量”和“低暂停时间”是一对相互竞争的目标(矛盾)。

    • 吞吐量大的时候:暂停时间会变长,用户线程暂停的时间就变长;
    • 暂停时间变短:吞吐量就变小,因为频繁切换线程也会导致总体的用户体验降低。
  • 高吞吐量较好因为这会让应用程序的最终用户感觉只有应用程序线程在做“生产性”工作。直觉上,吞吐量越高程序运行越快。

2. 不同的垃圾回收器概述


2.1 编年史

1999年随JDK1.3.1一 起来的是串行方式的Serial GC,它是第一款GC。ParNew垃圾收集器是Serial收集器的多线程版本

2002年2月26日,Parallel GC和Concurrent Mark Sweep GC跟随JDK1.4.2一起发布

Parallel GC在JDK6之后成为HotSpot默认GC。

2012年,在JDK1.7u4版本中,G1可用。

2017年,JDK9中G1变成默认的垃圾收集器,以替代CMS。

2018年3月,JDK10中G1垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。

2018年9月,JDK11发布。引入Epsilon垃圾回收器,又被称为"No-op (无操作) "回收器。同时,引入ZGC:可伸缩的低延迟垃圾回收器(Experimental)。

2019年3月,JDK12发布。 增强G1,自动返回未用堆内存给操作系统。同时,引入Shenandoah GC:低停顿时间的GC (Experimental)。

2019年9月,JDK13发布。增强ZGC,自动返回未用堆内存给操作系统。

2020年3月,JDK14发布。删除CMS垃圾回收器。扩展ZGC在macOS和Windows上的应用

2.2 垃圾回收器的分类

  • 串行回收器:Serial. Serial Old
  • 并行回收器:ParNew. Parallel Scavenge. Parallel Old
  • 并发回收器:CMS. G1

D:\Pictures\markdowmPic\image-20210617120042883.png

按分代区分

在这里插入图片描述

2.3 GC直接的配合战

只有最合适的,没有最好的。

在这里插入图片描述

为什么要有很多个收集器?不够吗? 因为Java的使用场景很多, 移动端,服务器等。所以就需要针对不同的场景,提供不同的垃圾收集器,提高垃圾收集的性能。

  • -xx:+PrintCommandLineFlags: 查看命令行相关参数(包含使用的垃圾收集器)
  • 使用命令行指令:jinfo 一flag [相关垃圾回收器参数] [进程id]
    • -XX:+UseSerialGC:表明新生代使用Serial GC ,同时老年代使用Serial Old GC
    • -XX:+UseParNewGC:标明新生代使用ParNew GC (现在已经被抛弃了)
    • -XX:+UseParallelGC:表明新生代使用Parallel GC
    • -XX:+UseParallelOldGC : 表明老年代使用 Parallel Old GC

3. 经典7回收器


3.1 Serial回收器:串行回收

单核CPU下的简单&高效

【配合】Serial Old GC 连续的收集器(复制算法)和连续的老年代收集器(标记压缩算法)

  • Serial收集器采用复制算法串行回收Stop-the-World机制的方式执行内存回收。

    • 复制算法在新生代中有所使用;
    • 串行回收是因为这就是Serial收集器;
    • STW会对用户体验造成较差的影响。
  • Serial old收集器。 Serial old收集器同样也采用了串行回收Stop the World机制,只不过内存回收算法使用的是标记-压缩算法

    • 老年代就是得压缩,把内存弄得规规整整的才好。

在这里插入图片描述

3.2 ParNew回收器:并行回收

Par是Parallel的缩写,New: 只能处理的是新生代

【配合】ParNew GC复制算法 + CMS GC(标记压缩算法;C:Concurrent)

  • ParNew收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。ParNew收集器在年轻代中同样也是采用复制算法Stop-the-World机制
  • 【区别】只存在并行回收上,里面的底层算法都是一样的。
    • 对于新生代,回收次数频繁,使用并行方式高效。
    • 对于老年代,回收次数少,使用串行方式节省资源。(CPU并行 需要切换线程,串行可以省去切换线程的资源)
  • -XX:ParallelGCThreads 限制线程数量,默认开启和CPU数据相同的线程数。.

在这里插入图片描述

3.3 Parallel回收器:吞吐量优先

可以给吞吐量做一个配置的回收器,专注于大吞吐量!

【配合】Parallel Scavenge GC (复制算法)和Parallel Ord (标记压缩)打配合

(框架比较独特,不能和串行&并行一起使用。)

  • Parallel Scavenge收集器同样也采用了复制算法并行回收Stop the World机制
    • 【不同】Parallel Scavenge收集 器的目标则是达到一个可控制的吞吐量(Throughput),它也被称为吞吐量优先的垃圾收集器。
    • 自适应调节策略
  • 主要适合在后台运算而不需要太多交互的任务。因此,常见在服务器环境中使用
  • 【1.8的默认垃圾回收器】证明其性能的优秀

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MWLWVzsY-1623992760313)(D:\Pictures\markdowmPic\image-20210617162744966.png)]

参数配置

  • -XX:+UseParallelGC手动指定 年轻代使用Parallel并行收集器执行内存回收任务。

  • -XX:+UseParallelOldGc
    

    手动指定老年代都是使用并行回收收集器。

    • 分别适用于新生代和老年代。默认jdk8是开启的。
    • 上面两个参数,默认开启一个,另一个也会被开启。 (互相激活)
  • -XX:ParallelGCThreads
    

    设置年轻代并行收集器的线程数。一般地,最好与CPU数量相等,以避免过多的线程数影响垃圾收集性能。

    • 在默认情况下,当CPU数量小于8个, Paralle lGCThreads 的值等于CPU数量。
    • 当CPU数量大于8个, ParallelGCThreads的值等于3+[5*CPU_ Count]/8]
  • -XX :MaxGCPauseMillis
    

    设置垃圾收集器最大停顿时间(即STW的时间)。单位是毫秒。

    • 为了尽可能地把停顿时间控制在MaxGCPauseMills以内,收集器在.工作时会调整Java堆大小或者其他一些参数。
    • 对于用户来讲,停顿时间越短体验越好。但是在服务器端,我们注重 高并发,整体的吞吐量。所以服务器端适合Parallel进行控制。该参数使用需谨慎
  • -XX:GCTimeRatio
    

    垃圾收集时间占总时间的比例(= 1 / (N + 1))用于衡量吞吐量的大小。

    • 取值范围(0, 100)。默认值99。
    • 与前一个-XX:MaxGCPauseMillis参数有一定矛盾性。暂停时间越长,Radio参数就容易超过设定的比例。
  • -XX:+UseAdaptiveSizePolicy
    

    设置Parallel Scavenge收集器具有自适应调节策略

    • 在这种模式下,年轻代的大小、Eden和Survivor的比例、晋升老年 代的对象年龄等参数会被自动调整,已达到在堆大小、吞吐量和停顿时间之间的平衡点。
    • 在手动调优比较困难的场合,可以直接使用这种自适应的方式,仅指 定虚拟机的最大堆、目标的吞吐量(GCTimeRatio)和停顿时间(MaxGCPauseMills),让虚拟机自己完成调优工作。

由图可知,以上的垃圾回收,都是具有STW特性的,会使得用户线程始终都会停顿那么一下。他们的不同只是在于有没有适配多线程(串串、冰川、并并)。

3.4 CMS回收器:低延迟

它第一次实现了让垃圾收集线程与用户线程同时工作。【尽可能缩短垃圾收集时用户线程的停顿时间。】

可以和串行and并行(Serial 、ParNew)二选一,标记清除算法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WkrrKeUF-1623992760314)(D:\Pictures\markdowmPic\image-20210617142036464.png)]

【过程】:

初始标记(Initial-Mark) 阶段:只标记【指向堆引用的直接对象】。STW的时间极短

在这个阶段中,程序中所有的工作线程都将会因为. “Stop-the-World"机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GCRoots能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快。

并发标记(Concurrent-Mark)阶段:从【堆引用直接对象】开始到对象图的遍历;

从GC Roots的 直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。

重新标记(Remark) 阶段:修正程序导致的引用集发生变化;STW的时间也比较短

由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。

并发清除( Concurrent-Sweep)阶段:清理时间——Sweep Time

此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间。由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。

目前所有的垃圾收集器都做不到完全不需要“Stop-the-World”,只是尽可能地缩短暂停时间

【优】并发收集、低延迟

【缺】内存碎片、使CPU变慢、无法处理浮动垃圾。

另外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,而是当堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了.

【成也萧何,败也萧何】CMS收集器的垃圾收集算法采用的是标记–清除算法,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,不可避免地将会产生一些内存碎片。 那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer) 技术,而只能够选择空闲列表(Free List) 执行内存分配。

3.5 小结

JDK14删除了CMS垃圾回收器

如果你想要最小化地使用内存和并行开销,请选Serial GC (Serial Old);
如果你想要最大化应用程序的吞吐量,请选Parallel GC (Parallel Old);
如果你想要最小化GC的中断或停顿时间,请选CMS GC (ParNew 新生代的)。

东西小,搞串行;业务大,高并发;体验好,吃美食。

4. G1垃圾回收器


自己的语言搞一搞

4.1 基本特性

【what】面向服务端,多核CPU大容量内存,高吞吐量。

【目标】G1设定的目标是在延迟可控的情况下获得尽可能高的吞吐量,所以才担当起“全功能收集器”的重任与期望

【why name】侧重点在于回收垃圾最大量的区间(Region),所以我们给G1一个名字:垃圾优先(Garbage First)

【分区算法】把堆内存分割为很多不相关的区域|Region| (物理上 不连续的)。使用不同的Region来表示Eden、幸存者0区,幸存者1区,老年代等

4.2 【优点】

并行与并发
  • 并行性: G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW
  • 并发性: G1拥有与应用程序交替执行的能力**,部分工作可以和应用程序同时执行**,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况。
分代收集:逻辑上是分代的,但是物理上是分隔开的(化整为零思想)
  • 从分代上看,G1依然属于分代型垃圾回收器,它会区分年轻代和老年代,年轻代依然有Eden区和Survivor区。
  • 但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是连续的,也不再坚持固定大小和固定数量。
空间整合
  • G1将内存划分为一个个的region。 内存的回收是以region作为基本单的。Region之间是复制算法,但整体上实际可看作是标记-压缩(Mark-Compact)算法,两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。尤其是当Java堆非常大的时候,G1的优势更加明显。
可预测的停顿时间模型(即:软实时soft real-time)
  • 这是G1相对于CMS的另一大优势,G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
  • 由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制。
  • G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以 及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。保证了G1 收集器在有限的时间内可以获取尽可能高的收集效率。
  • 相比于CMS GC,G1未必能做到CMS在最好情况下的延时停顿,但是最差情况要好很多。

HotSpot垃圾收集器里,除了G1以外,其他的垃圾收集器使用内置的JVM线程执行 GC的多线程操作,而G1 GC可以采用应用线程承担后台运行的GC工作,即当JVM的GC线程处理速度慢时,系统会调用应用程序线程帮助加速垃圾回收过程。

4.3 【缺点】

【占用的内存过多】G1无论是为了垃圾收集产生的内存占用(Footprint) 还是程序运行时的额外执行负载(overload) 都要比CMS要高。

【各自为王】平衡点在6~8GB之间:小内存CMS占优,大内存G1占优。

4.4 参数设置

  • -XX:+UseG1GC 手动指定使用G1收集器执行内存回收任务。
  • -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。

4.5 适用场景

  • 面向服务端应用,针对具有大内存、多处理器的机器。(在普通大小的堆里表现并不惊喜)
  • 如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒; ( G1通过每次只清理一部分而不是全部的Region的增量式清理来保证每次GC停顿时间不会过长)。
  • 用来替换掉JDK1.5中的CMS收集器; 在下面的情况时,使用G1可能比CMS好:
    ①超过50%的Java堆被活动数据占用;
    ②对象分配频率或年代提升频率变化很大;
    ③GC停顿时间过长(长于0. 5至1秒)。

【回收过程】young gc 一> young gc + concurrent mark 一> Mixed GC顺序,进行垃圾回收。

  1. 当年轻代的Eden区用尽时开始年轻代回收过程; G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收。然后从年轻代区间移动存活对象到Survivor区间或者老年区间,也有可能是两个区间都会涉及。
  2. 当堆内存使用达到一定值(默认45%)时,开始老年代并发标记过程。
  3. 标记完成马上开始混合回收过程。对于一个混合回收期,G1 GC从老年区间移动存活对象到空闲区间,这些空闲区间也就成为了老年代的一部分。和年轻代不同,老年代的G1回收器和其他GC不同,G1的老年代回收器不需要整个老年代被回收,一次只需要扫描/回收一小部分老年代的Region就可以了。同时,这个老年代Region是和年轻代一起 被回收的。

4.6 优化建议

年轻代大小

  • 避免使用-Xmn或-XX:NewRatio等相关选项显式设置年轻代大小。固定年轻代的大小会覆盖暂停时间目标

暂停时间目标不要太过严苛

  • G1 GC的吞吐量目标是90%的应用程序时间和10%的垃圾回收时间
  • 评估G1 GC的吞吐量时,暂停时间目标不要太严苛。目标太过严苛表示你愿意承受更多的垃圾回收开销,而这些会直接影响到吞吐量。

5. 垃圾回收器总结:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzp8fNY8-1623992760315)(D:\Pictures\markdowmPic\image-20210618115753374.png)]

G1:限定时间内完成大的吞吐量

5.1 正确的选择回收器

单核单机程序:Serial GC

多CPU,高吞吐量:并行Parallel GC

多CPU,快速响应:CMS

5.2 问面试:循序渐进:

垃圾回收的算法有哪些?如何判断对象是否可回收?

标记阶段、清除阶段

垃圾回收器工作的基本过程?

常用的参数有哪些?内存、堆、GC的参数。

GC日志分析:

-XX:PrintGCDetails  GC详细日志  -verbose:gc
-XX:PrintTimeStamps GC时间戳
-XX:PrintDateStamps GC时间戳
-XX:PrintHeapAtGC   堆的前后变化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

willorn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值