JVM -- CMS

并发的标记—清除(Concurrent Mark Sweep,缩写为 CMS)收集器,使得在整个收集的过程中只是很短的暂停应用的执行,可通过在 JVM 参数中设置-XX:UseConcMarkSweepGC 来使用此收集器,不过此收集器仅用于old和Perm(永生)的对象收集,并发的标记—清除较之Stop-The-World 的标记—清除复杂了很多,来看看:

  并发标记—清除做到的是在标记访问每一个节点时以及清除不活跃的对象时采用和应用并发的方式,仅需在初始化标记节点状态以及最终标记节点状态时需要暂停整个应用,因此其造成的应用的暂停的时间会比较的短。

  并发标记—清除为了保证尽量短的造成应用的暂停,首先从分配内存上做了改动,CMS提供了两个 free lists,一个用于存放小对象,另外一个则用于存放大对象,当 JVM 需要给对象分配内存时,则通过 free list 来找到可用的堆地址,并进行内存的分配以及将此地址从 free
list 删除,当 CMS 回收对象内存后,则将其相应的地址重新放入此 free list 中,这样的好处是在回收对象的时候不需要做对象的移动等,因此可以让回收过程并发的进行。

  接着来看看并发标记—清除的执行步骤:

1.Initial Marking --初始标记
  此步需要暂停整个应用,JVM 扫描整个 old generation 中根对象可直接访问到的对象,并对这些对象进行标记,对于标记的对象 CMS 采用一个外部的 bit 数组来进行记录。

2. Concurrent Marking --并发标记
  在初始化标记完毕后,CMS 恢复所有应用的线程,同时开始并发的对之前标记过的对象进行轮循,以标记这些对象可访问的对象。CMS 为了确保能够扫描到所有的对象,避免在 Initial Marking 中还有未标识到的对象,采用的方法为找到标记了的对象,并将这些对象放入 Stack 中,扫描时寻找此对象依赖的对象,如果依赖的对象的地址在其之前,则将此对象进行标记,并同时放入 Stack 中,如依赖的对象地址在其之后,则仅标记该对象。

  在进行 Concurrent Marking 时 minor GC 也可能会同时进行,这个时候很容易造成旧生代对象引用关系改变,CMS 为了应对这样的并发现象,提供了一个 Mod Union Table 来进行记录,在这个 Mod Union Table 中记录每次 minor GC 后修改了的 Card 的信息。

  在进行 Concurrent Marking 时还有可能会出现的一个并发现象是应用修改了旧生代中的对象的引用关系,CMS 中仍然采用 Card Table 的方式来进行记录,在 Card 中将某对象标识为 dirty 状态,但即使是这样仍然可能会出现一种现象导致不再被引用的对象仍然是 marked的状态:

3.Final Marking -- 最终标记
  此步需要暂停整个应用,由于在 Concurrent Marking 时应用可能会修改对象的引用关系或创建新的对象,因此需要把这些改变或新创建的对象也进行扫描,CMS 递归扫描 Mod Union Table 以及 Card Table 中 dirty 的对象,并进行标记。

4.Concurrent Sweeping -- 并发清除
  在完成了 Final Marking 后,恢复所有应用的线程,就进入到这步了,这步需要负责的是将没有标记的对象进行回收。

  回收过程是并发进行的,而 JVM 分配对象内存(尽管 CMS 仅用于 old generation,但有些时候会由于应用创建的对象过大导致直接分配到 old generation 的现象,另外一种现象就是 young generation 经过回收后需要转入 old generation 的对象)和 CMS 释放内存又都是操
作 free list,会产生 free list 竞争的现象,因此 CMS 在此增加了 Mutual exclusion locks,以 JVM分配优先。

  CMS 为了避免每次回收后回收到的大小都比之前分配出去的内存小,在进行 sweeping的时候,还会尽量的将相邻的块重新组装为一个块,sweeping 为了避免和 JVM 分配对象内存产生冲突,采用的方法为首先从 free list 中删除块,组装完毕后再重新放入块中,为了能够从 free list 中删除指定的块,CMS 将 free list 设计为了双向链表。

总结:

  CMS 中的耗时的过程都是和应用并发进行的,这也是 CMS 最突出的优点,使得其造成的应用的暂停时间比 Mark-Sweeping 的方式短了很多,但同时也意味着 CMS 会和应用线程争抢 CPU 资源, CMS 回收内存的方式也使得其很容易产生内存碎片,降低了空间的利用率,
另外就是 CMS 在回收时容易产生一些应该回收但需要等到下次 CMS 才能被回收掉的对象,例如上图中的 C 对象,称为“浮动垃圾“,这也就要求了采用 CMS 的情况下需要提供更多的可用的旧生代空间,总体来说 CMS 很适用于对响应时间要求很高、CPU 资源竞争不是很激烈以及内存空间相对更充足的系统。

  MS 为了降低和应用争抢 CPU 资源的现象发生,还提供了一种增量的模式,称为 i-CMS,在这种模式下,CMS 仅启动一个处理器线程来并发的扫描标记和清除,并且该线程在执行一小段时间后就会先将 CPU 使用权让出来,分多次多段的方式来完成整个扫描标记和清除
的过程,这样降低了对于 CPU 资源的消耗,但同时也降低了 CMS 的性能,因此仅适用于 CPU少的应用。
  CMS 为了减少产生的内存碎片,提高 jvm 空间的利用率,提供了一个整理碎片的功能,可通过在 jvm 中指定-XX:+ UseCMSCompactAtFullCollection (开启对内存空间的整理工作)来启动此功能,在启动了此功能后默认为每次 Full GC 的时候都会进行整理,也可以通过-XX:CMSFullGCsBeforeCompaction=来指定多少次 Full GC 后才执行整理,不过要注意的是,整理这个步骤是需要暂停整个应用的。

[-->  注 <--]

  Mod Union Table ----在并发标记阶段Minor GC 造成的对象之间引用的变化

  Card Table --- 在并发标记阶段应用程序本身造成对象之间引用的变化。

原文地址:https://www.cnblogs.com/plxx/p/4527065.html