JVM垃圾回收器详解

JVM垃圾回收器详解

一 垃圾回收器的组合关系

  1. 7款垃圾回收器与垃圾分代之间的关系
    在这里插入图片描述
  2. 垃圾收集器的组合关系
    在这里插入图片描述
    说明:
    ① 两个收集器间有连续表明它们可以搭配使用
    ② 其中Serial Old作为CMS出现Concurrent Mode Failure失败的后备预案
    ③ 红色虚线由于维护和兼容性测试的成本,在JDK8时将Serial+CMS,ParNew+Serial Old这两个组合声明为废弃,并在JDK9中中完全取消了这些组合的支持
    ④ 绿色虚线在JDK14中,弃用了Parallel Scavenge和Serial Old GC组合
    ⑤ 青色虚线在JDK14中,删除了CMS垃圾回收器
  3. 查看当前使用的回收器
    (1)查看命令行相关参数
    -XX:+PrintCommondLineFlags
    (2)查看垃圾回收器的指令
    jps查看进程,jinfo -flag 相关垃圾回收器参数 进程ID

二 Serial回收器-串行回收

(1)Serial收集器采用复制算法、串行回收和STW机制的方式执行内存回收
(2)Serial收集器作为HotSpot中Client模式下的默认新生代垃圾收集器
(3)Serial还提供用于执行老年代垃圾收集的Serial Old收集器, Serial Old收集器同样也采用了串行回收和STW机制,但内存回收算法采用的是标记-压缩算法
(4)Serial Old是运行在Client模式下默认的老年代的垃圾回收器;其在Server模式下主要有两个用途:
① 与新生代的Parallel Scavenge配合使用
② 作为老年代CMS收集器的后备垃圾收集方案
(5)Serial/Serial Old收集器
在这里插入图片描述
这个收集器是一个单线程的收集器,不仅仅只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时必须暂停其他所有的工作线程,直到它收集结束,可以通过设置参数:-XX:+UseSerialGC指定使用该收集器,其包括老年代的回收器

三 ParNew回收器-并行回收

(1)ParNew回收器采用并行回收的方式执行内存回收,在年轻代中同样也是采用复制算法和STW机制,它是很多JVM运行在Server模式下新生代的默认垃圾收集器
(2)ParNew/SerialOld收集器
在这里插入图片描述
① 对于新生代,回收次数频繁,使用并行方式高效
② 对于老年代,回收次数少,使用串行方式节省资源,可以省去切换线程的资源
(3)ParNew和Serial二者选择
① 多CPU情况下,选择ParNew,由于其可以并行回收,可以更快的完成垃圾收集,提升程序的吞吐量
② 在单个CPU情况下,使用Serial,由于CPU不需要频繁的做任务切换,因此可以有效避免多线程交互过程中产生的一些额外开销

四 Parallel Scavenge回收-吞吐量优先

(1)Parallel Scavenge收集器也采用了复制算法、并行回收和STW机制其目标是达到一个可控制的吞吐量,也被称为吞吐量优先的垃圾收集器,其自适应调节策略也是它与ParNew的一个重要区别
(2)高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务,常见在服务器环境中使用,例如:批量处理、订单处理、工资交付以及科学计算应用程序等
(3)Parallel Old收集器采用了标记-压缩算法,基于并行回收和STW机制
(4)Parallel Scavenge/Parallel Old收集器在这里插入图片描述
(5)Parallel 参数设置
① -XX:+UseParallelGC,手动指定年轻代使用Parallel并行收集器执行内存回收任务
② -XX:+UseParallelOldGC,手动指定老年代使用并行回收收集器,和ParallelGC参数设置是相互激活的,只需要设置一个就可以了
③ -XX:ParellelGCThreads,设置年轻代并行收集器的线程数,一般最好和CPU数量相等,避免过多的线程数影响垃圾收集性能;在默认情况下,当CPU数量小于8时,ParallelGCThreads的值等于CPU数量,当CPU数量大于8时,ParallelGCThreads的值等于3+[5*CPU_Count]/8
④ -XX:MaxGCPauseMillis设置垃圾收集器最大停顿时间,该参数使用需谨慎
⑤ -XX:GCTimeRatio垃圾收集时间占总时间的比例(1/(N+1)),用于衡量吞吐量的大小
⑥ -XX:+UseAdaptiveSizePolicy,设置Parallel Scavenge收集器具有自适应调节策略
总之,以上参数都可以在JVM官方文档中进行查找

五 CMS垃圾回收器-低延时

(1)HotSpot虚拟机中第一款真正意义上的并发收集器,第一次实现了让垃圾收集线程和用户线程同时工作,CMS收集器尽可能缩短垃圾收集时用户线程的停顿时间,时间越短就越适合于用户交互的程序
(2)CMS的垃圾算法采用标记-清除算法,也有STW机制
(3)CMS工作原理
在这里插入图片描述
说明:
① 初始标记阶段:程序中所有的工作线程会因为STW机制出现短暂的暂停,这个阶段的任务主要是标记出GC Roots能直接关联到的对象
② 并发标记阶段:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行
③ 重新标记阶段:在并发标记阶段,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也比并发标记阶段的时间短
④ 并发清除阶段:此阶段清理删除标记阶段判断的已经死亡的对象,释放空间,由于不需要移动存活对象,所以这个阶段是可以与用户线程同时并发的
(4)CMS的优缺点
由于最耗费时间的并发标记和并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。由于在垃圾收集阶段用户线程没有中断,在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用,所以CMS收集器不能像其他收集器一样等到老年代几乎完全被填满在进行收集,而是当堆内存使用率达到某一个阙值时,便开始进行回收,确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是预留的内存无法满足程序需要,就会出现一次Concurent Mode Failure失败,这时虚拟机就会启动后备预案,临时启动Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了
① 优点
并发收集;低延迟
② 缺点
会产生内存碎片,导致并发清除后,用户线程可用的内存空间不足无法分配大对象的情况下,不得不提前触发Full GC
CMS收集器对CPU资源非常敏感,在并发阶段,虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低
CMS收集器无法处理浮动垃圾,在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,会导致新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间
(5)CMS参数设置
① -XX:+UseConcMarkSweepGC手动指定使用CMS收集器
② -XX:CMSLnitiatingOccupanyFraction设置堆内存使用率的阙值达到这个阙值就开始回收
③ -XX:+UseCMSCompactAtFullCollection指定在执行完Full GC后对内存空间进行压缩整理,避免内存碎片的产生,由于内存压缩整理过程无法并发执行,所以停顿时间就会变长
④ -XX:ParallelCMSThreads设置CMS的线程数,默认启动的线程数是(ParallelGCThreads+3)/ 4

六 G1回收器-区域化分代式

  1. G1回收器的特点
    (1)并行和并发
    并行性:G1在回收期,可以有多个GC线程同时工作,有效利用多核计算能力,用户线程会有STW
    并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况
    (2)分代收集
    ① **从分代上看,G1依然属于分代型垃圾回收器,**年轻代依然有Eden区和Survivor区,但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是连续的,也不再坚持固定的大小和数量
    将堆空间分为若干个区域,这些区域中包含了逻辑上的年轻代和老年代
    它同时兼顾年轻代和老年代,对比其他收集器,或者工作自年轻代或者再老年代
    (3)空间整合
    G1将内存划分为一个个的region,内存的回收是以region为基本单位的,region之间是复制算法,但整体上可以看做是标记-压缩算法,两种算法都可以避免内存碎片
    (4)可预测的停顿时间模型
    G1除了追求低停顿外还可以建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
    ① 由于分区,G1可以只选取部分区域进行内存回收,缩小了回收的范围,对于全局停顿情况的发生得到较好的控制
    ② G1跟踪各个region里的回收所获得空间大小以及回收所需时间的经验值,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的region,保证了G1收集器在有限的时间内可以
    获取尽可能高的收集效率

    (5)G1回收器的缺点
    ① G1无论是为了垃圾收集产生的内存占用还是程序运行时的额外执行负载都要比CMS高
    ② 在小内存应用上CMS的表现会优于G1,而G1在大内存应用上则发挥其优势
  2. 参数设置
    (1)-XX:+UseG1GC,手动指定G1收集器执行内存回收任务
    (2)-XX:G1HeapRegionSize,设置每个Region的大小,值是2的幂,范围是1-32MB之间,目标是根据最小的Java堆大小划分出约2048个区域,默认是堆内存的1/2000
    (3)-XX:MaxGCPauseMillis,设置期望达到的最大GC停顿时间指标,默认值是200毫秒
    (4)-XX:ConcGCThreads,设置并发标记的线程数,将n设置为并行垃圾回收线程数的1/4左右
    (5)-XX:ParallelGCThread,设置STW工作线程数的值,最多为8
    (6)-XX:InitiatingHeapOccupancyPerent,设置触发并发GC周期的Java堆占用率阙值,超过此值,触发GC,默认值为45
  3. region的使用介绍
    (1)化整为零,使用G1收集器时,它将整个Java划分为约2048个大小相同的独立Region快,每个块大小根据堆空间的实际大小而定,所有的Region大小相同,且在JVM生命周期内不会被改变。虽然还保留了新生代和老年代的概念,但他们不再是物理隔离了,都是一部分Region的集合,通过Region的动态分配方式实现逻辑上的连续
    (2)Region实例图
    在这里插入图片描述
    ① 一个Region有可能属于Eden,Survivor或者Old/Tenured内存区域,但是一个Region只可能属于一个角色
    ② G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域,主要用于储存大对象,如果超过1.5个region就放到H中
    ③ 对于堆中的大对象,默认会直接放在老年代,如果它是一个短期存在的大对象,就会对垃圾收集器造成负面影响,所以G1划分了H区域来存放大对象,如果一个H区放不下一个大对象,那么G1就会寻找连续的H区来存储
  4. Remembered Set介绍
    (1)Region不可能被孤立,一个Region中的对象可能被其他任意Region中对象引用,所以会扫描整Java堆,会降低Minor GC效率
    (2)为解决这个问题,JVM使用Remembered Set来避免全局扫描,每个Region都有一个对应的Remembered Set
    (3)每次Reference类型数据写操作时,都会产生一个Write Barrier暂时中断操作,然后检查将要写入的引用指向的对象是否和该Reference类型数据在不同的Region,如果不同,通过CardTable把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中
    (4)当进行垃圾收集时,在GC根节点的枚举范围加入Remembered Set就可以保证不进行全局扫描也不会有遗漏
    在这里插入图片描述
  5. G1回收器垃圾回收过程
    G1 GC垃圾回收过程主要包括以下三个环节:
    ① 年轻代GC
    ② 老年代并发标记过程
    ③ 混合回收
    在这里插入图片描述
    说明:
    (1)应用程序分配内存,当年轻代的Eden区用尽开始年轻代回收过程,G1的年轻代收集阶段是一个并行的独占式收集器。在年轻代回收期,G1 GC暂停所有应用程序线程,启动多线程执行年轻代回收,然后从年轻代区间移动存活对象到Survivor区或老年区,也有可能两个区都会涉及
    (2)当堆内存使用达到一定值时,开始老年代并发标记过程
    (3)标记完成马上开始混合回收过程,对于一个混合回收期,G1 GC从老年区移动存活对象到空闲区,这些空闲区就成为老年代的一部分,G1的老年代回收器不需要整个老年代回收,一次只需要扫描回收一小部分老年代的Region就可以了
  6. G1垃圾回收过程详解
    (1)年轻代GC阶段
    JVM启动时,G1先准备好Eden区,程序在运行中不断创建对象到Eden区,当Eden区空间耗尽,G1会启动一次年轻代垃圾回收程,年轻代垃圾回收只会回收Eden区和Survivor区
    YGC时,首先G1停止应用程序的执行,G1创建回收集,回收集是指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段
    在这里插入图片描述
    回收过程:
    ① 第一阶段:扫描根
    根是指static变量指向的对象,正在执行的方法调用链上的局部变量,根引用连同RSet记录的外部引用作为扫描存活对象的入口
    ② 第二阶段:更新RSet
    处理dirty card queue中的card,在年轻代回收的时候会对该队列中的所有card进行处理,以更新RSet,此阶段完成后,RSet可以准确的反映老年代对所在的内存分段中对象的引用
    ③ 第三阶段:处理RSet
    识别被老年代对象指向的Eden中的对象,这些被指向的Eden中的对象被认为是存活的对象
    ④ 第四阶段:复制对象
    对象树被遍历,Eden区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区内存段中存活的对象如果年龄未达到阙值,年龄会加1,达到阙值会被复制到Old区中空的内存分段,如果Survivor区内存空间不够,Eden区的部分数据会直接晋升到老年代空间
    ⑤ 第五阶段:处理引用
    处理soft、weak、phantom、final、JNI weak等引用,最终Eden区空间的数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程可以达到内存整理的效果,减少碎片
    (2)并发标记阶段
    ① 阶段一:初始标记阶段
    标记从根节点直接可达的对象,这个阶段会发生STW,并且会触发一次年轻代GC
    ② 阶段二:根区域扫描
    G1 GC扫描Survivor区直接可达的老年代区域对象,并标记被引用的对象,这一过程必须在young GC之前完成
    ③ 阶段三:并发标记
    在整个堆中进行并发标记,此过程可能被young GC中断,在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那这个区域会被立即回收,同时,会计算每个区域的对象活性即区域中存活对象的比例
    ④ 第四阶段:再次标记
    由于应用程序持续进行,需要修正上一次的标记结果,是STW的,G1采用比CMS更快的初始快照算法-SATB
    ⑤ 第五阶段:独占清理
    计算各个区域的存活对象和GC回收比例,并进行排序,识别可以混合回收的区域,为下阶段做铺垫,也是STW的
    ⑥ 第六阶段:并发清理阶段
    识别并清理完全空闲的区域
    (3)混合回收阶段
    在这里插入图片描述
    当越来越多的对象晋升到老年代Old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC,该算法并不是一个Old GC,除了回收整个young Region,还会回收一部分old region,可以选择那些old region进行收集,从而对垃圾回收的耗时时间进行控制
    ① 并发标记结束后,老年代中百分百为垃圾的内存分段被回收了,部分垃圾的内存分段被计算出来,默认情况下,这些老年代的内存分段会分8次被回收
    ② 混合回收的回收集包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段,混合回收的算法和年轻代回收的算法一样,只是回收集多了老年代的内存分段
    ③ 由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的内存分段,垃圾占内存分段比例越高越会先被回收,并且有一个阙值默认为65%,决定内存分段是否被回收,如果垃圾占比太低,意味着存活的对象占比高,在复制的时候hi花费更多时间
    ④ 混合回收不一定要进行8次,有一个阙值-XX:G1HeapWastePercent,默认值为10%,允许整个堆内存中有10%的空间被浪费,如果发现可以回收的垃圾占堆内存的比例低于10%,则不再进行混合回收
    (4)Full GC阶段
    ① G1的初衷是避免出现Full GC,但如果以上方式不能正常工作,G1 就会停止应用程序的执行,使用单线程的内存回收算法进行垃圾回收,性能会非常低,应用程序停顿时间会很长
    ② 当堆内存太小,G1在复制复活对象的时候没有空的内存分段可用,则会回退到Full GC,可以通过增大内存解决
    ③ 导致Full GC的原因有两个:
    Evacuation(回收阶段)的时候没有足够的to-space(空间)来存放晋升的对象
    并发处理过程完成之前空间耗尽
  7. 7种经典垃圾回收器总结和调优
    在这里插入图片描述
    在这里插入图片描述
    (1)如何选择垃圾回收器
    ① 优先调整堆的大小让JVM自适应完成
    ② 内存小于100MB,使用串行收集器
    ③ 单核、单机程序并且灭有停顿时间的要求使用串行收集器
    ④ 多CPU,需要高吞吐量,允许停顿时间超过1秒,选择并行或者JVM自己选择
    (2)GC日志分析
    ① -XX:+PrintGC:输出GC日志类似于:-verbose:gc
    ② -XX:+PrintGCDetails:输出GC的详细日志
    ③ -XX:+PrintGCTimesStamps:输出GC的时间戳
    ④ -XX:+PrintGCDateStamps:输出GC的时间戳
    ⑤ -XX:+PrintHeapAtGC:进行GC前后打印堆的信息
    ⑥ -Xloggc:…/logs/gc.log:日志文件的输出路径
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值