十二、七款经典垃圾收集器

2 篇文章 0 订阅

GC分类

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

在这里插入图片描述

  • 串行回收指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直到垃圾收集结束
    • 在单CPU处理器或较小的应用内存等硬件平台不是特别优越的场合,串行回收器的性能表现可以超过并行回收器和并发回收器.所以,串行回收默认被应用在客户端的Client模式下的JVM中
    • 在并发能力较强的CPU上,并行回收器产生的停顿时间要短与串行回收器
  • 和串行回收相反,并行收集可以运用多个CPU同时执行垃圾回收,提升了应用的吞吐量.不过并行回收仍然与串行回收一样,采用独占式,使用了STW机制

按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器

在这里插入图片描述

  • 并发式垃圾回收器与应用程序线程交替工作,尽可能减少应用程序的停顿时间
  • 独占式垃圾回收器(STW)一旦运行,就停止应用程序中所有的用户线程,直到垃圾回收过程结束

按碎片处理方式分,可分为压缩式垃圾回收器和非压缩式垃圾回收器

  • 压缩式垃圾回收器在回收完成后,对存活对象进行压缩整理,消除回收后的碎片
  • 非压缩式的垃圾回收器不进行这不操作

按工作的内存区间分,又可分为年轻代垃圾回收器和老年代垃圾回收器

评估GC的性能指标

  • 吞吐量: 运行用户代码的时间占总运行时间比例 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)
  • 垃圾收集开销: 吞吐量的补数,垃圾收集所用时间与重运行时间的比例
  • 暂停时间: 执行垃圾收集时,程序的工作线程被暂停的时间
  • 收集频率: 相对于应用程序的执行,收集操作发生的频率
  • 内存占用: Java堆区所占内存大小
    快速: 一个对象从诞生到被回收所经历的时间

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

七款经典的垃圾收集器

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

Serial回收器: 串行回收

  • Serial收集器是最基本,历史最悠久的垃圾收集器
  • 为HotSpot中Client模式下的默认新生代垃圾收集器
  • Serial收集器采用复制算法,串行回收和STW机制的方式执行内存回收
  • 除了年轻代职位,Serial收集器还提供于执行老年代垃圾收集的Serial Old收集器.Serial Old收集器同样采用串行回收和STW机制,只不过内存回收算法使用的是标记-压缩算法
    • Serial Old是运行在Client模式下默认的老年代垃圾回收器
    • Serial Old在Server模式下主要有两个用途: 1,与新生代的Parallel Scavenge配合使用,2. 作为老年代CMS收集器的预备收集方案
  • 这个收集器是一个单线程的收集器,但是它的单线程不仅仅说明他只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是她在进行垃圾收集时,必须暂停其他所有工作线程,知道收集结束
  • 使用 -XX:+UseSerialGC 指定年轻代和老年代都是用串行收集器
    在这里插入图片描述

优势

  • 简单而高效(与其他收集器的单线程比), 在单个CPU的环境来说,Serial收集器由于没有线程交互开销,获得最高的单线程收集效率
  • 运行在Client模式下的虚拟机是个不错的选择

总结

  • 目前已经不用,而且在限定单核CPU才可以用,现在都不是单核的了
  • 对于交互较强的应用而言,这种垃圾收集器是不能接受的,一般Java web程序不会采用该垃圾收集器

ParNew回收器: 并行回收

  • 如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew收集器则是多线程版本
  • 除了采用并行回收方式执行外,两款垃圾收集器几乎没有任何区别.在年轻代同样也是采用复制算法,STW机制
  • ParNew是很多JVM运行在Server模式下新生代的默认垃圾收集器

在这里插入图片描述

  • 对于新生代,回收次数频繁,使用并行方式高效
  • 对于老年代,回收次数少,使用串行方式节约资源
  • ParNew收集器运行在多核CPU环境下,由于可以充分利用多核CPU等物理硬件资源优势,可以更快的完成垃圾收集,提升程序的吞吐量
  • 但在单核CPU的环境下,ParNew收集器不比Serial收集器高效.因为CPU需要不停的作任务切换,需要一下额外的开销
  • -XX:+UserParNewGC 指定使用ParNew收集器,不影响老年代, -XX:ParalleGCThreads 现在线程数量,默认开启和CPU数据相同的线程数

Parallel回收器: 吞吐量优先

  • Parallel Scavenge收集器采用了复制算法,并行回收和STW机制
  • 和ParNew收集器不同,Parallel Scavenge收集器的目标是达到一个可控的吞吐量
  • 自适应调节策略也是一个重要区别
  • 高吞吐量可以高效利用CPU时间,尽快完成程序的运算任务,主要适合在后台运行而不需要太多交互的任务.常见于服务器环境中.如执行批量处理,订单处理,工资支付,科学技术的应用程序
  • 提供了Parallel Old来代替老年代的Serial Old收集器
  • Parallel Old收集器采用了标记-压缩算法,也是基于并行回收和STW机制
  • 在Java8中,默认是此垃圾收集器

在这里插入图片描述

参数配置

  • -XX:+UseParallelGC-XX:+UseParallelOldGC 指定使用Parallel收集器,开启一个,另一个也会被开启
  • -XX:ParallelGCThreads 设置年轻代并行收集器的线程数,CPU小于8个,最好与CPU数量相等.大于8个则为 3+(5*CPU_Count)/8
  • -XX:MaxGCPauseMillis 设置垃圾收集器最大停顿时间,单位毫秒.谨慎使用
  • -XX:GCTimeRatio垃圾收集时间占总时间的比例,衡量吞吐量的大小
  • -XX:+UseAdaptiveSizePolicy 开启自适应调节策略
    • 在该模式下,年轻代的大小,Eden和Survivor的比例,晋升老年代的对象年龄等参数会被自动调整,已达到在堆大小,吞吐量和停顿时间直接的平衡点
    • 在手动调优比较困难的场合,可以直接使用该自适应的方式,仅指定虚拟机的最大堆,目标的吞吐量和停顿时间,让虚拟机自己完成调优工作

CMS回收器: 低延迟

  • JDK1.5推出,该收集器是HotSpot虚拟机中第一款真正意义上的并发收集器,第一次实现了让垃圾收集线程与用户线程同时工作
  • CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间.停顿时间越短就越适合与用户交互的程序,良好的响应速度能提升用户体验
    • 目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短.CMS收集器就非常符合这类应用的需求
    • 采用标记-清除算法,也有STW机制
  • 然而,CMS作为老年代的收集器,无法与新生代收集器Parallel Scavenge配合工作(架构原因),所以JDK1.5中使用CMS收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个

在这里插入图片描述

工作原理

分为四个主要阶段 : 初始标记阶段, 并发标记阶段, 重新标记阶段和并发清除阶段

  • 初始标记阶段: 在这个阶段中,程序中所有的工作线程都将会因为STW机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GC Roots能直接关联到的对象.一旦标记完成之后就会恢复应用线程.由于直接关联对象比较小,这里执行速度非常快
  • 并发标记阶段: 从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集器线程一起并发运行
  • 重新标记阶段: 由于在并发标记阶段,程序的工作线程会和垃圾收集线程同时运行或者交叉运行.因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录.这个阶段的停顿时间通常会比初始标记阶段稍长,但远比并发标记阶段时间短
  • 并发清除阶段: 此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存.由于不需要移动存活对象,所以该阶段也是可以与用户线程同时并发的

为很么不使用标记-压缩算法

当并发清除的时候,此时整理内存的话,原来的用户线程使用的内存还怎么使用?要保证用户的线程能继续执行,前提是他运行的资源不受影响.标记-压缩算法更适合STW的场景下使用

CMS的优点

  • 并发收集
  • 低延迟

CMS的弊端

  • 会产生内存碎片. 导致并发清除后,用户线程可用的空间不足,在无法分配大对象的情况下,不得不提前触发Full GC.
  • CMS收集器对CPU资源非常敏感. 在并发阶段,虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,中吞吐量降低
  • CMS收集器无法处理浮动垃圾. 可能出现并发模式失败而导致另一次Full GC的产生.在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有及时回收,只能在下一次执行GC时收集这些垃圾

CMS的参数设置

  • -XX:+UseConcMarkSweepGC ,指定使用CMS收集器
  • -XX:CMSlinitiatingOccupanyFraction, 设置堆内存使用率的阈值,一旦达到该阈值,便开始回收,
    • JDK5及以前版本默认值为68,当老年代空间使用率达到68%时,会执行一次CMS回收.JDK6及以上默认值为92%
    • 如果内存增长缓慢,可以设置一个稍大的值,可以有效较低CMS的触发频率,减少老年代回收的次数可以明显改善应用程序.反正内存使用率增长很快,则应该降低这个阈值,避免频繁发生.通过该选项可以有效降低Full GC的执行次数
  • -XX:+UseCMSCompactAtFullCollection 用于指定在执行多少次Full GC 后对内存空间进行压缩整理
  • -XX:ParallelCMSThreads 设置CMS的线程数量.默认启动线程数是 (年轻代并行收集器的线程数 + 3) / 4, 当CPU资源比较紧张时,收到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕

小结

  • 想要最小化的使用内存和并行开销,选用 Serial GC
  • 想要最大化应用程序的吞吐量,选用 Parallel GC
  • 想要最小化GC的中断或停顿时间,选用 CMS GC

G1回收器: 区域化分代式

介绍

  • 应用程序应对的业务越来越庞大,复杂,用户越来越多,没有GC就不能保证应用程序的正常进行,而经常造成STW的GC又跟不上实际的需求,所有才会不断尝试对GC优化.
  • 为了适应不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间,同时兼顾良好的吞吐量
  • 官方给G1设定的目标是在延迟可控的情况下获得尽可能高的吞吐量,所以才担当起全功能收集器的重任与期望
  • G1是一款面向服务端应用的垃圾收集器,主要针对配备多核CPU及大容量内存的机器,以极高概率满足GC停顿时间的同时,还兼具高吞吐量的性能特征
  • JDK9及以后的默认垃圾回收器,JDK8中使用 -XX:+UseG1GC 启用

工作原理

  • G1是一个并行回收器,把堆内存分割为很多不相关的区域(Region),物理上不连续的,使用不同的Region来表示Eden,幸存者0区,幸存者1区,老年代等
  • G1 GC有计划的避免在整个Java堆中进行全区域的垃圾收集.G1跟踪各个Region里的垃圾堆积的价值大小(回收所获得空间大小以及回收所需时间的价值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region
  • 这种方法侧重点在于回收垃圾最大量的区间
  • 提供了三种垃圾回收模式: YoungGC, Mixed GC和FullGC,在不同条件下触发

在这里插入图片描述

优点

  • 使用了分区算法
  • 并行与并发
    • 并行性: G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力,次数用户线程STW,
    • 并发性: G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,一般来说不会再整个回收阶段发生完全阻塞应用程序的情况
  • 分代收集
    • G1依然属于分代垃圾收集器,会区分年轻代和老年代,年轻代依然有Eden区和Survivor区.但从堆的结构上看,不要求整个Eden区,年轻代和老年代是连续的,不在坚持固定大小和固定数量
    • 将堆空间分为若干个区域(Region),这些区域中包含了逻辑上的年轻代和老年代.
    • 同时还兼顾年轻代和老年代,对比其他收集器,或工作在年轻代,或工作在老年代

在这里插入图片描述

  • 空间整合
    • CMS: 标记-清除算法,内存碎片,若干次GC后进行一次碎片整理
    • G1将内存划分为一个个的region.内存的回收是以region为基本单位的,region之间使用复制算法.但整体可以看作 标记-压缩算法, 两种算法都能避免内存碎片,有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC, 尤其当Java堆非常大时,G1的优势更明显
  • 可预测的停顿时间模型
    • 这是G1相对CMS的另一大优势,G1处理追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不超过N毫秒
      • 由于分区原因,G1可以只选择部分区域进行内存回收,缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制
      • G1跟踪各个Region里的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region.保证G1收集器在有限的时间内可以获取尽可能高的收集效率
      • 相比CMS, G1未必能做到在CMS最好情况下的延迟停顿,但是最差情况要好很多

缺点

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

G1参数设置

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

调优

  1. 开启G1垃圾收集器
  2. 设置堆的最大内存
  3. 设置最大的停顿时间

G1回收器的适用场景

  • 面向服务端应用,针对具有大内存,多处理器的机器普通大小的堆里表现一般
  • 主要的应用是需要低GC延迟,并具有大堆的应用程序提供解决方案
  • 用来替换掉JDK1.5中的CMS收集器,在下面情况下,G1可能比CMS好
    • 超过50%的Java堆被活动数据占用
    • 对象分配频率或年代提升频率变化很大
    • GC停顿时间过长, 0.5秒至1秒
  • HotSpot垃圾收集器中,除了G1外,其他的垃圾收集器使用内置的JVM线程执行GC的多线程,而G1 GC采用应用线程承担后台运行的GC工作,当JVM的GC线程处理速度慢时,系统会调用应用程序线程帮助加速垃圾回收过程

分区Region

化整为零

使用G1收集器时,它将整个Java堆划分为约2048个大小相同的独立Region块,每个Region大小根据堆空间的实际大小而定,整体被控制在1MB至32MB之间,且为2的N次幂,即1MB,2MB,4MB,8MB,16MB,32MB,可以通过-XX:G1HeapRegionSize设置,所有的Region大小相同,且在JVM生命周期内不会被改变
虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离了,他们都是region的集合,通过region的动态分配方式实现逻辑上的连续
在这里插入图片描述

  • 一个region可能属于Eden,Survivor或Old/Tenured内存区域.但是一个region只可能属于一个角色
  • G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域.主要用于存储大对象,如果超过1,5个region,就放入该内存区域
  • 设置H区域的原因
    • 对于堆中的大对象,默认直接会被分配到老年代,但是如果他是一个短期存在的大对象,就会对垃圾收集器造成负面影响.为了解决这个问题,G1划分了一个Humongous区,专门用来存放大对象.如果一个H区装不下一个大对象,那么G1会寻找连续的H区来存储,为了能找到连续的H区,有时候不得不启动Full GC,G1的大多数行为都把H区作为老年代的一部分来看待

G1回收器垃圾回收过程

主要包括三个环节:

  • 年轻代GC
  • 年代并发标记过程
  • 混合回收
  • (如果需要,单线程,独占式,高强度的Full GC还是继续存在的.它针对GC的评估失败提供了一种失败保护机制,即强力回收)

在这里插入图片描述

顺时针 年轻代 --> 年轻代+并发标记过程 --> 混合回收 顺序进行垃圾回收

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

记忆集与写屏障

  • 一个对象被不同区域引用的问题(分代引用问题)
  • 一个region不可能是孤立的,一个region中的对象可能被其他任意region中对象引用,判断对象存活时,是否需要扫描整个Java堆才能保证准确性?
  • 在其他的分代收集器也存在这样的问题,G1更突出
  • 回收新生代也不得不同时扫描老年代
  • 这一会降低混合回收的效率
  • 解决方案
    • 无论G1还是其他分代收集器,JVM都是使用RememberdSet来避免全局扫描
    • 每个region都有一个对应的RememberdSet
    • 每次reference类型数据写操作时,都会产生一个wirte barrier暂时中断操作
    • 然后检查将要写入的引用指向的对象是否和该reference类型数据在不同的region(其他收集器:检查老年代对象是否引用了新生代)
    • 如果不同,通过cardTable把相关引用信息记录到引用指向对象所在region对象的记忆集中
    • 当进行垃圾收集时,在GC 根节点的枚举范围加入记忆集,就可以保证不进行全局扫描,也不会有遗漏

在这里插入图片描述

G1回收过程

一、 年轻代GC

JVM启动时,G1先准备好Eden区,程序在运行过程中不断创建对象到Eden去,当Eden空间耗尽时,G1会启动一次年轻代垃圾回收过程
年轻代垃圾回收只会回收Eden区和Survivor区
首先G1停止应用程序的执行,G1创建回收集,回收集指需要被回收的内存分段的集合,年轻代回收过程的回收集包含年轻代Eden区和Survivor区所有的内存分段

然后开始如下回收过程:

  • 扫描根
    • 根是指static变量指向的对象,正在执行的方法调用链条上的局部变量等,根引用连同RSet记录的外部引用作为扫描存活对象的入口
  • 更新RSet
    • 处理dirty card queue 中的card,更新RSet,此阶段完成后,RSet可以准确的反应老年代所在的内存分段中对象的引用
      • dirty card queue: 对于应用程序的引用赋值语句,JVM会在之前和之后执行特殊的操作再dirty card queue中入队一个保存了对象引用信息的card,在年轻代回收的时候,G1会对dirty card queue中所有的card进行处理,以更新RSet,保证RSet实时准确反映引用关系,不在引用赋值时直接更新RSet,是为了性能的需要,RSet的处理需要线程同步,开销会很大,使用队列性能会好很多
  • 处理RSet
    • 识别被老年代对象指向的Eden中的对象,这些被指向的Eden汇总的对象被认为是存活的对象
  • 复制对象
    • 对象数被遍历, Eden 区内存段中存活的对象会被复制到Survivor区中空的内存分段,Survivor区中存活的对象如果年龄未达到阈值,年龄会加1, 达到阈值的会被复制到old区中空的内存分段,如果Survivor区空间不够,Eden去的部分数据会直接晋升为老年代空间
  • 处理引用
    • 处理 Soft, Weak, Phantom, Final, JNI Weake 等引用,最终Eden区空间内数据为空,GC停止工作,而目标内存中的对象都是连续存储的,没有碎片,所以复制过程可以达到内存整理的效果,减少碎片

二、并发标记过程

  1. 初始标记阶段: 标记从根节点直接可达的对象,这个阶段是STW的,并且会触发一次年轻代GC
  2. 根区域扫描: G1 GC 扫描Survivor区直接可达的老年代区域对象,并标记被引用的的对象,这一过程必须在young GC之前完成
  3. 并发标记: 在整个堆中进行并发标记(和应用程序并发执行),此过程可能被young GC 中断,在并发标记阶段, 若发现区域对象中的所有对象都是垃圾,那这个区域可能会被立即回收. 同时,并发标记过程中,会计算每个区域的对象活性(区域中存活对象的比例)
  4. 再次标记: 由于应用程序持续进行,需要修正上一次的标记结果,是STW的.G1中采用了比CMS更快的初始快照算法SATB
  5. 独占清理: 计算各个区域的存活对象和GC回收比例,并进行排序,识别可以混合回收的区域.为以下阶段做铺垫,是SWT的
    这个阶段不会实际上去做垃圾收集
  6. 并发清理阶段: 识别并清理完全空闲的区域

三、混合回收

在这里插入图片描述
当越来越多的对象晋升为老年代old Region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即Mixed GC.改算法并不是一个Old GC, 除了回收整个young Region, 还会回收一部分的Old Region. 可以对垃圾回收的耗时进行控制.注意该GC不是 Full GC

  • 并发标记结束后,老年代中百分比为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算出来了,默认情况下,这些老年代的内存分段会分8次(-XX:G1MixedGCCountTarget 设置)被回收
  • 混合回收的回收集包括八分之一的老年代内存分段,Eden区内存分段,Survivor区内存分段.混合回收的算法和年轻代回收的算法完全一样,只是回收集多了老年代的内存分段
  • 由于老年代中的内存分段默认分8次回收,G1会优先回收垃圾多的内存分段.垃圾占内存分段比例越高,越会被先回收,并且有一个阈值会决定内存分段是否被回收,默认是65%.达到该比例才会被回收.如果垃圾占比太低,意味着存活的对象占比高.在复制的时候会花费更多的时间
  • 混合回收并不一定要进行8次,有个阈值默认为10%,允许整个堆内存中有10%的空间被浪费,意味着如果发现可以回收的垃圾占堆内存的比例低于10%,则不进行混合回收,因为GC会花费很多的时间但回收到的内存却很少

可选过程、Full GC

G1的初衷就是要避免Full GC 的出现,但是如果上述方式不能正常工作,G1会停止应用程序的执行,使用单线程的内存回收算法进行垃圾回收,性能会非常差STW时间会很长
要避免Full GC的发生,一旦发生需要进行调整,堆内存太小会发生Full GC, 当G1在复制存活对象的时候没有空的内存分段可用,则会回退到Full GC,可用通过增大内存解决

导致G1 Full GC的原因可能有两个

  1. 回收阶段的时候没有足够的 to-space 来存放晋升对象;
  2. 并发处理过程完成前空间耗尽

补充

回收阶段其实本也有想过设计成与用户程序一起并发执行,但做起来比较复杂,考虑到G1只是回收一部分的region, 停顿时间是用户可控制的,所以并不迫切去实现,而选择把这个特性放到G1之后出现的低延迟垃圾收集器中(ZGC).还考虑G1不是仅仅面向低延迟,停顿用户线程能够最大幅度提高垃圾收集效率,为了保证吞吐量所以才选择完全停顿用户线程的实现方案

G1回收器优化建议

  • 年轻代大小
    • 避免使用 -Xmn 或 -XX:NewRatio 等相关选项显示设置年轻代大小
    • 固定年轻代的大小会覆盖暂停时间目标
  • 暂停时间目标不要太过于严苛
    • G1 GC 的吞吐量目标是90% 的应用程序时间和10%的垃圾回收时间
    • 评估G1 GC 的吞吐量时,暂停时间目标不要太严苛.目标太过于严苛表示你愿意承受更多的垃圾回收开销,会直接影响到吞吐量

回收器总结

垃圾收集器分类作用位置使用算法特点适用场景
Serial串行运行作用于新生代复制算法响应速度优先适用于单CPU环境下的client模式
ParNew并行运行作用于新生代复制算法响应速度优先多CPU环境Server模式下与CMS配合使用
Parallel并行运行作用于新生代复制算法吞吐量优先适用于后台运算而不需要太多交互的场景
Serial Old串行运行作用于老年代标记-压缩算法响应速度优先适用于单CPU环境下的Client模式
Parallel Old并行运行作用于老年代标记-压缩算法吞吐量优先适用于后台运算而不需要太多交互的场景
CMS并发运行作用于老年代标记-清除算法响应速度优先适用于互联网或 B/S 业务
G1并发、并行运行作用于新生代、老年代标记-压缩算法、复制算法响应速度优先面向服务端应用

在这里插入图片描述

  1. 两个收集器间有连线,表面他们可以搭配使用: Serial/Serial Old, Serial/CMS, ParNew/CMS, Parallel Scavenge/Serial Old, Parallel Scavenge/Parallel Old, G1
  2. 其中Serial Old作为 CMS出现失败后的预备方案
  3. (红色虚线)由于维护和兼容性测试的成本,在JDK8时将Serial/CMS, ParNew/Serial Old两个组合声明为废弃.在JDK9中完全移除这些组合
  4. (绿色虚线)JDK14中,废弃了Parallel Scavenge / Serial Old组合
  5. (青色虚线)JDK14中, 删除了CMS垃圾回收器
  6. GC发展阶段 Serial -> parallel (并行) -> CMS (并发) -> G1 -> ZGC

垃圾回收器选择

  • Java垃圾收集器的配置对于JVM优化来说是一个很重要的选择,选择合适的垃圾收集器可以让JVM的性能有一个很大的提升
  • 怎么选择
    • 优先调整堆的大小让JVM自适应完成
    • 如果内存小于100M, 使用串行收集器
    • 如果是单核,单机程序,并没有停顿时间的要求,串行收集器
    • 如果是多CPU,需要高吞吐量,允许停顿时间超过1秒,选择并行或者JVM自己选择
    • 如果是多CPU,追求停顿时间,需快速响应,延迟不能超过一秒,并发收集器
    • 官方推荐G1,性能高,现在互联网项目,基本都是使用G1
  • 没有最好的收集器,没有万能的收集,调优永远是针对特定场景,特定需求,不存在一劳永逸的收集器

GC日志分析

在这里插入图片描述

在这里插入图片描述

常用日志分析工具: GCViewer、GCEasy、GCHisto、GCLogViewer 、Hpjmeter、garbagecat等。

革命性的ZGC

在尽可能对吞吐量影响不大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟.
ZGC收集器是一款基于region内存布局的,不设分代,使用读屏障, 染色指针和内存多重映射等技术来实现可并发的标记-压缩算法,以低延迟为首要目标的一款垃圾收集全

ZGC工作过程: 并发标记 – 并发预备重分配 – 并发重分配 – 并发重映射

ZGC几乎在所有地方并发执行,处理初始标记时STW,所以停顿时间几乎就耗费在初始标记上,该部分的实际时间非常少

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值