Ceragon 的个人空间 Help

学习 CMS GC

概述

作为经典的垃圾回收器,虽然无法精确控制 STW 的时间,但是 GC 过程对 mutator 线程影响较小,因此吞吐量和内存占用都有明显优势。

通过查看 OpenJDK11 的源码,来一探 CMS 的执行过程。

大纲

  • cms 的触发条件

  • ....

cms 的触发条件

也就是老年代 gc 的触发条件。cms GC 的第一个阶段是初始标记,且该阶段需要 STW,对应的是以下代码

class VM_CMS_Initial_Mark: public VM_CMS_Operation { virtual void doit(); }

而上面的代码会在 VMThread 线程中执行,理由是父类 VM_CMS_Operation 代码如下:

class VM_CMS_Operation: public VM_Operation { }

初始标记的触发条件

初始标记的触发和 VM_CMS_Initial_Mark 类初始化有关,因此反向查看它的调用。以下方法是唯一调用

void CMSCollector::collect_in_background(GCCause::Cause cause) { switch (_collectorState) { case InitialMarking: { VM_CMS_Initial_Mark initial_mark_op(this); VMThread::execute(&initial_mark_op); } break; } }

接着查看 collect_in_background 方法的调用

void ConcurrentMarkSweepThread::run_service() { while (!should_terminate()) { // 睡眠,并等到下一次 cms 循环开始 sleepBeforeNextCycle(); // 被唤醒了,判断是 fullGC 还是 cms gc GCCause::Cause cause = _collector->_full_gc_requested ? _collector->_full_gc_cause : GCCause::_cms_concurrent_mark; // 执行 gc _collector->collect_in_background(cause); } }

如果关注点在触发条件的话,和 sleepBeforeNextCycle 有关系

void ConcurrentMarkSweepThread::sleepBeforeNextCycle() { while (!should_terminate()) { // 默认值是 2000 if(CMSWaitDuration >= 0) { wait_on_cms_lock_for_scavenge(CMSWaitDuration); } else { wait_on_cms_lock(CMSCheckInterval); } if (_collector->shouldConcurrentCollect()) { // 条件为真后被唤醒 return; } } }
bool CMSCollector::shouldConcurrentCollect() { if (_full_gc_requested) { return true; } if (!UseCMSInitiatingOccupancyOnly) { if (stats().valid()) { if (stats().time_until_cms_start() == 0.0) { return true; } } else { if (_cmsGen->occupancy() >= _bootstrap_occupancy) { return true; } } } if (_cmsGen->should_concurrent_collect()) { return true; } CMSHeap* heap = CMSHeap::heap(); if (heap->incremental_collection_will_fail(true /* consult_young */)) { return true; } if (MetaspaceGC::should_concurrent_collect()) { return true; } if (CMSTriggerInterval >= 0) { //TODO: } return false; }
Last modified: 27 December 2023