Connor学JVM - 垃圾收集器

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学JVM系列,这个系列记录了我的JVM基础知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/WVqh7,话不多说我们马上开始!

垃圾收集器

在这里插入图片描述

垃圾收集器组合总结:

  • Serial + Serial Old
  • ParNew + CMS + Serial Old
  • Parallel Scavenge + Parallel Old (吞吐量优先) JDK8中默认的垃圾收集器组合
  • Serial + CMS (JDK8,声明为废弃,JDK9取消) (CMS JDK14 被删除)
  • ParNew + Serial Old (JDK8,声明为废弃,JDK9取消)
  • G1 (JDK9 成为默认的垃圾收集器)
  • Parallel Scavenge + Serial Old(JDK14弃用)
1.Serial收集器

在这里插入图片描述

特点

(1)针对新生代的收集器,基于复制算法

(2)单线程收集,进行垃圾收集时必须暂停所有工作线程,Stop The World

应用场景

(1)依然是HotSpot在Client模式下默认的新生代收集器;

(2)优于其他收集器的地方: 简单高效(与其他收集器的单线程相比);

(3)对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率;

(4)在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是可以接受的

(5)可以通过参数-XX:+UseSerialGC 显式使用Serial收集器

2.ParNew收集器

在这里插入图片描述

特点

(1)针对新生代的收集器

(2)Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等)和Serial完全一样

(3)新生代采用复制算法,老年代采用标记-整理算法

应用场景

(1)服务端模式下许多虚拟机首选的新生代收集器

(2)与作为老年代收集器的CMS搭配。ParNew收集器是激活CMS后的默认新生代收集器

(3)在单CPU环境下效率不如Serial收集器,因为线程之间的交互存在开销

使用参数

-XX:+UseConcMarkSweepGC:强制指定CMS激活后的默认收集器

-XX:ParallelGCThreads:指定垃圾收集的线程数,默认开启的收集线程数与CPU数量相同

3.Parallel Scavenge收集器

在这里插入图片描述

特点

(1)针对新生代的收集器

(2)并行的多线程收集器

(2)关注吞吐量,吞吐量的计算:运行代码时间 / (运行代码时间 + 运行垃圾收集时间)

(3)采用复制算法

应用场景

高吞吐量可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合后台运算而不需要过多交互的分析任务

使用参数

-XX:MaxGCPauseMillis:一个大于0的毫秒数,收集器将尽力保证内存回收时间不超过用户设定值。注意不要设置的太小,即让垃圾收集时间太短,这样做会导致垃圾收集更频繁,牺牲吞吐量和新生代空间

-XX:GCTimeRatio:设置一个正整数,表示用户期望虚拟机消耗在垃圾收集上的时间不超过程序运行时间的 1 / (1 + N),默认为99

-XX:+UseAdaptiveSizePolicy:开关参数,开启后开启垃圾收集的自适应调剂策略:虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整新生代大小、Eden与Survivor区的比例等参数,以提供最合适的停顿时间或最大的吞吐量

4.Serial Old收集器

在这里插入图片描述

(1)Serial收集器的老年代版本,单线程收集器,基于标记 - 整理算法

(2)主要供客户端模式下的HotSpot虚拟机使用

(3)在服务端模式下可以有两种用途:

  • JDK5及之前与Parallel Scavenge收集器搭配使用
  • 作为CMS收集器失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用
5.Parallel Old收集器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BFI1Jo22-1661218543319)(F:\yhy925\dailylife\Work\1.Java\JVM\Resources\3.垃圾收集器\8.Parallel Scavenge.png)]

(1)Parallel Scavenge收集器的老年代版本,支持多线程并行收集,基于标记-整理算法

(2)JDK6及之后用来替代Serial Old收集器

(3)在注重吞吐量或CPU资源较为稀缺的场合,优先考虑 Parallel Scavenge + Parallel Old组合

6.CMS(Concurrent Mark Sweep)收集器

特点

(1)以获取最短回收停顿时间为目标的收集器,针对老年代,基于标记-清除算法,并发收集、低停顿

(2)适用于目前的互联网网站和基于浏览器的B/S系统的服务端上,提供尽可能短的系统停顿时间,提高服务的响应速度,改善交互体验

运作过程

在这里插入图片描述

(1)初始标记。标记GC Roots直接关联到的对象,速度很快

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

(3)重新标记。修正并发标记期间,因用户程序继续运行导致标记产生变动的那一部分对象的标记记录,停顿时间比初始标记阶段稍长,但远比并发标记阶段时间短

(4)并发清除。清除标记阶段判断的已经死亡的对象,因为不需要移动存活对象,可以与用户线程同时并发

缺点

1.对CPU资源非常敏感,并发阶段虽不会导致用户线程停顿,但会占用一部分线程导致应用程序变慢,降低吞吐量

(1)CMS默认启动的回收线程数是 (CPU数量 + 3) / 4

(2)如果CPU数多于四个,则回收线程数只占用不少于25%的CPU运算资源,随着CPU数量增加而下降

(3)如果CPU数不足四个,则可能会影响用户程序的运行效率,因为需要在CPU高负载的情况下再分出一部分运算能力执行收集器线程

2.无法处理浮动垃圾,有可能导致Concurrent Mode Failure失败

(1)浮动垃圾:在并发标记和并发清除阶段,收集线程和用户线程同时运行,这就会导致在清理的同时不断产生新的垃圾对象,而这些新的垃圾对象只能留到下一次垃圾收集时再清理

(2)同样因为垃圾收集与用户线程同时运行,需要预留足够内存空间给用户线程,因此CMS收集器不能等老年代几乎完全被填满了再收集,需要为老年代的内存空间设置一个阈值,大于这个比例后就需要开始垃圾收集

  • JDK5默认设置下,CMS收集器当老年代使用了68%的空间后就会被激活,也可以通过参数-XX:CMSInitiatingOccupancyFraction修改这个阈值
  • JDK6,这个阈值提高到92%,但这样又会带来新问题:预留的空间无法满足分配新对象的需要,就会出现一次并发失败(Concurrent Mode Failure),此时虚拟机将会启动后备预案:冻结用户线程的执行,启动Serial Old收集器来重新进行老年代的垃圾收集

3.基于标记 - 清除算法可能导致收集结束后产生大量空间碎片,无法分配空间进而导致Full GC,可通过两个参数辅助解决(JDK9已废弃)

  • -XX:+UseCMSCompactAtFullConnection:默认开启,用于在Full GC时开启内存碎片的合并整理,但内存整理必须移动存活对象,无法并发,导致停顿时间变长
  • -XX:CMSFullGCsBeforeCompaction:要求CMS收集器在执行过若干次(参数设置数量,默认0,即每次都整理)不整理空间的Full GC后,下一次Full GC前整理碎片
7.G1(Garbage First)收集器

特点

(1)面向服务端的整堆收集器,JDK9之后的默认收集器

(2)并行与并发。G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop The World停顿的时间,通过并发的方式让程序与垃圾收集同时执行

(3)分代收集。分代概念在 G1 中依然得以保留。虽然 G1 可以独立管理整个 GC 堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次 GC 的旧对象以获取更好的收集效果。

(4)空间整合。G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。

(5)可预测的停顿。 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒

分区设计

(1)G1放弃固定大小和数量的分代区域划分,而是把连续的Java堆划分为等大的独立空间Region,每一个Region都可以根据需要充当Eden、Survivor或老年代空间,收集器能够针对不同角色的Region采用不同的处理策略

(2)Region中还有一类特殊的 Humongous Region,专门用于存储大对象(大小超过一个Region容量的一半),而超过了整个Region容量的超大对象,将会被存放在N个连续的Humongous Region之中,G1的大多数行为都把Humongous Region作为老年代的一部分来看待

(3)每个Region的大小可以通过参数 -XX:G1HeapRegionSize 设定,取值范围为1MB - 32MB,且应为2的N次幂

在这里插入图片描述

为什么G1可以实现可预测的停顿时间模型

(1)将Region作为单次回收的最小单元,即每次收集到的内存空间都是Region大小的整数倍,避免整个Java堆中进行全区域的垃圾收集

(2)有计划地回收

  • 计算各个Region里面的垃圾堆积的“价值”:回收后能够获得的空间大小以及回收所需时间的经验值
  • 根据价值在后台维护一个优先级列表
  • 每次根据用户设定允许的收集停顿时间内,优先处理回收价值最大的Region
  • 通过-XX:MaxGCPauseMills指定设定的收集停顿时间,默认200毫秒

TAMS

(1)保证垃圾收集线程和用户线程并发执行期间创建新对象的内存分配

(2)G1为每一个Region设计了两个名为TAMS(Top at Mark Start)的指针,把Region中的一部分空间划分出来用于并发回收过程中的新对象分配,并发回收时新分配的对象地址都必须在这两个指针位置以上,因为G1收集器默认在这个地址以上的对象都是被显式标记过的,即存活的

运行过程

(1)初始标记

  • 标记GC Roots能直接关联到的对象
  • 修改TAMS指针,方便下一阶段分配新对象
  • 停顿线程,但耗时较短

(2)并发标记

  • 从GC Roots开始对堆中对象进行可达性分析,递归扫描整个堆中的对象图,找出要回收的对象
  • 扫描完成后,还要重新处理在并发时引用有变动的对象
  • 耗时较长,但可与用户程序并发执行

(3)最终标记

  • 对用户线程做短暂的暂停
  • 处理并发阶段结束后遗留下的少部分引用变动的对象

(4)筛选回收

  • 负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户期望的停顿时间指定回收计划
  • 可自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空Region中,再清理
  • 因为上述操作涉及存活对象的移动,必须暂停用户线程,由多条收集线程并行完成
8.CMS和G1的对比

都十分关注停顿时间的控制

不同点:

(1)G1可以指定最大停顿时间、分Region的内存布局、按收益动态确定回收集

(2)G1从整体上看基于标记-整理算法,局部上看基于标记-复制算法;CMS基于标记-清除算法

(3)G1不会产生内存空间碎片,垃圾收集完成后能提供规整的可用内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ConnorYan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值