因为热爱所以坚持,因为热爱所以等待。熬过漫长无戏可演的日子,终于换来了人生的春天,共勉!!!
1.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
⑥. CMS收集器在JDK9中被废弃,在JDK14中被移除
2.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) 执行内存分配。
⑥.既然Mark Sweep(标记清除)会造成内存碎片,那么为什么不把算法换成Mark Compact(标记压缩)呢?
答案其实很简答,因为当并发清除的时候,用Compact整理内存的话,会改变内存地址,原
来的用户线程使用的内存会受影响,要保证用户线程能继续执行,前提的它运行的资源不受影响。
3. CMS优缺点
①. 优点:并发收集、低延迟
②. CMS的弊端:
-
会产生内存碎片,导致清理后无法分配大对象情况下,不得不提前触发Full GC
-
**CMS收集器对CPU资源非常敏感。**在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变 慢,总吞吐量会降低
-
CMS收集器无法处理浮动垃圾。可能出现"Concurrent Mode Failure" 失败而导致另一次Full GC的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间
4. 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 默认启动的线程数是(Parallel****GCThreads+3)/4
- ParallelGCThreads 是年轻代并行收集器的线程数。当CPU 资源比较紧张时,受到CMS收集器线程的影响,应用程序的性能在垃圾回收阶段可能会非常糟糕)
5.JDK后续版本CMS的变化
- JDK9新特性: CMS 被标记为Deprecate了
➢如果对JDK 9及以上版本的HotSpot虚拟机使用参数- XX:+UseConcMarkSweepGC来开启CMS收集器的话,用户会收到一个警告信息,提示CMS未来将会被废弃。
- JDK14新特性: 删除CMS垃圾回收器
➢移除了CMS垃圾收集器,如果在JDK14中使用-XX: +UseConcMarkSweepGC的话,JVM不会报错,只是给出一个warning信息,但是不会exit。JVM会自动回退以默认GC方式启动JVM
参考视频 : 尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机)
参考书籍 : 深入理解Java虚拟机