JVM18_CMS低延迟垃圾收集器、概述、原理、优缺点、参数设置、三色标记、ASTB 和 Incremental Update、记忆集与卡表(一)

简介: ①. CMS概述②. CMS过程(原理)③. CMS优缺点④. CMS参数设置

①. CMS概述


①. 在JDK1.5时期, HotSpot推出了一款在强交互应用中几乎可认为有划时代意义的垃圾收集器: CMS (Concurrent 一Mark 一 Sweep)收集器,这款收集器是HotSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作


②. CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。


③. CMS的垃圾收集算法采用标记一清除算法,并且也会" stop一the一world"


④. 不幸的是,CMS 作为老年代的收集器,却无法与JDK 1.4.0 中已经存在的新生代收集器Parallel Scavenge配合工作,所以在JDK 1. 5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个


⑤. 在G1出现之前,CMS使用还是非常广泛的。一直到今天,仍然有很多系统使用CMS GC


微信图片_20220108143740.png


  • ⑥. CMS收集器在JDK9中被废弃,在JDK14中被移除


②. CMS过程(原理)


①. 初始标记(Initial一Mark)仅仅只是标记出和GCRoots能直接关联到的对象,有STW现象、暂时时间非常短


②. 并发标记(Concurrent一Mark)阶段:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行(并发标记阶段有三色标记,下文有记录)


③. 重新标记(Remark) 阶段:有些对象可能开始是垃圾,在并发标记阶段,由于用户线程的影响,导致不是垃圾了,这里需要重新标记的是这部分对象,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短


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


⑤. 补充说明:


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


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


(在并发标记阶段一开始不是垃圾,最后变成了垃圾)


③. CMS优缺点


  • ①. 优点:并发收集、低延迟


  • ②. CMS的弊端:


会产生内存碎片


CMS收集器对CPU资源非常敏感


(在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低)


CMS收集器无法处理浮动垃圾。可能出现"Concurrent Mode Failure" 失败而导致另一次Full GC的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间


③.区分两个注意事项


并发标记阶段,在遍历GCRoots,用户线程也在执行,若此时遍历过一个对象发现没有引用,但由于用户线程并发执行,这期间可能导致遍历过的这个对象又被其他对象引用,所以才需要重新标记阶段再遍历一次看又没有漏标记的,否则就会导致被重新引用的对象被清理掉

浮动垃圾:在并发标记阶段一开始不是垃圾,最后变成了垃圾(属于多标的情况)


④. CMS参数设置


①. -XX:+UseConcMarkSweepGc:手动指定使用CMS收集器执行内存回收任务

(开启该参数后会自动将一XX: +UseParNewGc打开。即: ParNew (Young区用) +CMS (0ld区用) +Serial 0ld的组合)


②. -XX:CMSlnitiatingOccupanyFraction:设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收


JDK5及以前版本的默认值为68,即当老年代的空间使用率达到68%时,会执行一次CMS 回收。JDK6及以上版本默认值为92%

如果内存增长缓慢,则可以设置一个稍大的值,大的阈值可以有效降低CMS的触发频率,减少老年代回收的次数可以较为明显地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该降低这个阈值,以避免频繁触发老年代串行收集器。因此通过该选项便可以有效降低Full GC的执行次数

③. -XX:+UseCMSCompactAtFullCollection:用于指定在执行完Full GC后对内存空间进行压缩整理,以此避免内存碎片的产生。不过由于内存压缩整理过程无法并发执行,所带来的问题就是停顿时间变得更长了


④. -XX:CMSFullGCsBeforeCompaction:设置在执行多少次Full GC后对内存空间进行压缩整理


⑤. -XX:ParallelCMSThreads:设置CMS的线程数量

(CMS 默认启动的线程数是(ParallelGCThreads+3)/4,ParallelGCThreads 是年轻代并行收集器的线程数。当CPU 资源比较紧张时,受到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕)


相关文章
|
2天前
|
存储 监控 安全
深入理解Java虚拟机(JVM)原理
深入理解Java虚拟机(JVM)原理
|
7天前
|
Java 编译器
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
10 1
|
1天前
|
存储 监控 安全
深入理解Java虚拟机(JVM)原理
深入理解Java虚拟机(JVM)原理
|
1天前
|
监控 算法 Java
Java虚拟机垃圾收集机制深度解析
在Java的世界中,垃圾收集是确保内存管理高效运行的关键机制之一。本文将深入探讨Java虚拟机的垃圾收集机制,包括其工作原理、常见的垃圾收集算法以及调优实践。我们将基于最新的研究数据和实验结果,提供对垃圾收集器性能的比较分析,并讨论如何根据不同应用场景进行优化。通过逻辑严密的分析,我们旨在为Java开发者提供实用的指导,以帮助他们更好地理解和掌握这一关键技术。
|
7天前
|
存储 监控 算法
深入理解Java虚拟机(JVM)原理与调优技巧
深入理解Java虚拟机(JVM)原理与调优技巧
|
7天前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
11 0
|
3天前
|
缓存 算法 Java
JVM内存溢出(OutOfMemory)异常排查与解决方法
JVM内存溢出(OutOfMemory)异常排查与解决方法
|
12天前
|
存储 Java C++
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为几个不同的区域
【6月更文挑战第24天】Java JVM管理内存分7区:程序计数器记录线程执行位置;虚拟机栈处理方法调用,每个线程有独立栈;本地方法栈服务native方法;Java堆存储所有对象实例,垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息;运行时常量池存储常量;直接内存不属于JVM规范,通过`java.nio`手动管理,不受GC直接影响。
21 5
|
10天前
|
存储 Java 对象存储
jvm内存模型剖析
当线程cpu时间片执行完后,线程进入休眠状态,当再次唤醒时,通过程序计数器确定指令执行到哪一行,然后继续往下执行。
19 1
|
11天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
22 2