目录
前言
工作步骤
缺点
问题
上一章节我们讲了串/并行GC,这一章节说下CMS GC。看前思考一个问题,并行GC与CMS GC的区别在哪里。
什么是CMS收集器
CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。
XX:+UseConcMarkSweepGC
其对年轻代采用并行 STW 方式的 mark-copy(标记-复制)算法,对老年代主要使用并发 mark-sweep(标记-清除)算法。
CMS GC 的设计目标是避免在老年代垃圾收集时出现长时间的卡顿,主要通过两种手段来达成此目标:
1.不对老年代进行整理,而是使用空闲列表 (freelists) 来管理内存空间的回收。
2.在 mark-and-sweep (标记-清除) 阶段的大部分工作和应用线程一起并发执行。
也就是说,在这些阶段并没有明显的应用线程暂停。但值得注意的是,它仍然和应用线程争抢CPU 。默认情况下,CMS 使用的并发线程数等于 CPU 核心数的 1/4。如果服务器是多核 CPU,并且主要调优目标是降低 GC 停顿导致的系统延迟,那么使用 CMS 是个很明智的选择。进行老年代的并发回收时,可能会伴随着多次年轻代的 minor GC
1、4阶段是没有并发这俩字的是因为,这两个阶段都需要去做SWT。
综上,CMS GC的初始标记阶段主要进行GC Roots直接关联对象的标记,这是后续初始化标记的基础,同时记录卡表以优化效率。
总结:这个阶段伴随着 STW 暂停。初始标记的目标是标记所有的根对象,包括根对象直接引用的对象,以及年轻代指向老年代的对象(老年代单独回收) 。
综上,允许CMS GC的并发标记GC与应用程序并发执行,可以将Stop The World时间最小化,降低应用程序延迟。它通过并发控制和多轮标记来逐步进行最后对象标记。
总结:在此阶段,CMS GC 遍历老年代,标记所有的存活对象,从前一阶段“Initial Mark”找到的根对象开始算起。“并发标记”阶段,就是与应用程序同时运行,不用暂停的阶段
综上所述,CMS GC预清理阶段主要是初期阶段产生的浮动垃圾,减少下一阶段的工作量,从而达到大约停顿时间。
总结:此阶段同样是与应用线程并发执行的,不需要停止应用线程。 因为前一阶段[并发标记] 与程序并发运行,可能有一些引用关系已经发生了改变。如果在并发标记过程中引用关系发生了变化,JVM 会通过“Card (卡片)”的方式将发生了改变的区域标记为“脏”区,这就是所谓的 卡片标记 (Card Marking)
综上,最终标记是停止所有应用线程进行的标记,它对并发标记做修改,准确描述标识所有不受影响的对象,为并发清理阶段做准备。
总结:最终标记阶段是此次 GC 事件中的第二次 (也是最后一次)STW停顿。本阶段的目标是完成老年代中所有存活对象的标记。因为之前的预清理阶段是并发执行的,有可能 GC 线程跟不上应用程序的修改速度。所以需要一次 STW 暂停来处理各种复杂的情况。
通常 CMS 会尝试在年轻代尽可能空的情况下执行 Final Remark阶段,以免连续触发多次 STW 事件。
综上,CMS GC的清理阶段可以避免清理所的停顿时间,同时恢复造成垃圾对象并腾出空间。
总结:此阶段与应用程序并发执行,不需要 STW 停顿。JVM 在此阶段删除不再使用的对象,并回收他们占用的内存空间
综上,CMS GC的重置阶段通过重置相关数据结构,为下一步CMS做好准备。
总结:此阶段与应用程序并发执行,重置 CMS 算法相关的内部数据,为下一次 GC 循环做准备。
占比
总得来说,CMS回收器减少了回收的停顿时间,但是降低了堆空间的利用率。
初始化阶段中查找年轻代对年老代对象的引用,到底是否有使用RSet集合?
这个当时在望山搜了一些资料,发现有的说使用了有的说只在并发阶段使用了。本想去询问下客服,后来发现中国区客服貌似下线了。。。
最后终于发现在jdk1.8(实际是JDK 7 u4之后,为了方便记忆)引用了CMSBitmap。这个类似于RSet集合,但不是同一种数据格式。
1.我查看了OpenJDK1.8的hotspot源码在顶级类CMSCollector中,有以下注释:
// _initial_mark performs the initial marking of roots and uses // the new _bitmap to mark live objects. // _initial_mark执行根和使用的初始标记 //新的_bitmap来标记活动对象。
这说明了初始标记会使用CMSBitMap。
2.Oracle的官方文档中也提到:
The first phase of CMS is called initial mark. This phase only marks GC roots (stack variables, etc.) and objects directly reachable from them. Because this phase runs concurrently with the mutator threads, it must be conservative to avoid perturbing the running application. Thus, it uses card marks from the write barrier to identify object graphs that may have changed. //翻译:CMS的第一阶段称为初始标记。这个阶段只标记GC根(堆栈变量等)和可直接访问的对象。因为这个阶段与mutator线程并发运行,所以它必须是保守的,以避免干扰正在运行的应用程序。因此,它使用写屏障中的卡片标记来识别可能已经更改的对象图。
这里说初始标记会使用write barrier记录的card mark信息,也就是CMSBitMap。
3.各种Books wie Tunning Java Performance也对此有描述综上所述我重新验证了JDK1.8 CMS GC 初始标记使用CMSBitMap的说法来自官方代码文档等多方佐证。请您放心,这一信息是可以靠谱的。如您还有任何疑问,欢迎随时提出,我会继续提供参考依据。
CMSBitmap和RSet集合的区别
官方参考文档:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html
课程链接:阿里云盘分享
今天就到这里吧,感觉有用的小伙伴可以点个赞,你的支持就是我更新的最大动力!