副标题[/!--empirenews.page--]
经常有同学会问,为啥我的应用 Old Gen 的使用占比没达到 CMSInitiatingOccupancyFraction 参数配置的阈值,就触发了 CMS GC,表示很莫名奇妙,不知道问题出在哪?

其实 CMS GC 的触发条件非常多,不只是 CMSInitiatingOccupancyFraction 阈值触发这么简单。本文通过源码全面梳理了触发 CMS GC 的条件,尽可能的帮你了解平时遇到的奇奇怪怪的 CMS GC 问题。
先抛出一些问题,来吸引你的注意力。
- 为什么 Old Gen 使用占比仅 50% 就进行了一次 CMS GC?
- Metaspace 的使用也会触发 CMS GC 吗?
- 为什么 Old Gen 使用占比非常小就进行了一次 CMS GC?
触发条件
CMS GC 在实现上分成 foreground collector 和 background collector。foreground collector 相对比较简单,background collector 比较复杂,情况比较多。
下面我们从 foreground collector 和 background collector 分别来说明他们的触发条件:
说明:本文内容是基于 JDK 8
说明:本文仅涉及 CMS GC 的触发条件,至于算法的具体过程,以及什么时候进行 MSC(mark sweep compact)不在本文范围
foreground collector
foreground collector 触发条件比较简单,一般是遇到对象分配但空间不够,就会直接触发 GC,来立即进行空间回收。采用的算法是 mark sweep,不压缩。
background collector
说明 background collector 的触发条件之前,先来说下 background collector 的流程,它是通过 CMS 后台线程不断的去扫描,过程中主要是判断是否符合 background collector 的触发条件,一旦有符合的情况,就会进行一次 background 的 collect。
- void ConcurrentMarkSweepThread::run() {
- ...//省略
- while (!_should_terminate) {
- sleepBeforeNextCycle();
- if (_should_terminate) break;
- GCCause::Cause cause = _collector->_full_gc_requested ?
- _collector->_full_gc_cause : GCCause::_cms_concurrent_mark;
- _collector->collect_in_background(false, cause);
- }
- ...//省略
- }
每次扫描过程中,先等 CMSWaitDuration 时间,然后再去进行一次 shouldConcurrentCollect 判断,看是否满足 CMS background collector 的触发条件。CMSWaitDuration 默认时间是 2s(经常会有业务遇到频繁的 CMS GC,注意看每次 CMS GC 之间的时间间隔,如果是 2s,那基本就可以断定是 CMS 的 background collector)。
- void ConcurrentMarkSweepThread::sleepBeforeNextCycle() {
- while (!_should_terminate) {
- if (CMSIncrementalMode) {
- icms_wait();
- if(CMSWaitDuration >= 0) {
- // Wait until the next synchronous GC, a concurrent full gc
- // request or a timeout, whichever is earlier.
- wait_on_cms_lock_for_scavenge(CMSWaitDuration);
- }
- return;
- } else {
- if(CMSWaitDuration >= 0) {
- // Wait until the next synchronous GC, a concurrent full gc
- // request or a timeout, whichever is earlier.
- wait_on_cms_lock_for_scavenge(CMSWaitDuration);
- } else {
- // Wait until any cms_lock event or check interval not to call shouldConcurrentCollect permanently
- wait_on_cms_lock(CMSCheckInterval);
- }
- }
- // Check if we should start a CMS collection cycle
- if (_collector->shouldConcurrentCollect()) {
- return;
- }
- // .. collection criterion not yet met, let's go back
- // and wait some more
- }
- }
那 shouldConcurrentCollect() 方法中都有哪些条件呢?
- bool CMSCollector::shouldConcurrentCollect() {
- // 第一种触发情况
- if (_full_gc_requested) {
- if (Verbose && PrintGCDetails) {
- gclog_or_tty->print_cr("CMSCollector: collect because of explicit "
- " gc request (or gc_locker)");
- }
- return true;
- }
- // For debugging purposes, change the type of collection.
- // If the rotation is not on the concurrent collection
- // type, don't start a concurrent collection.
- NOT_PRODUCT(
- if (RotateCMSCollectionTypes &&
- (_cmsGen->debug_collection_type() !=
- ConcurrentMarkSweepGeneration::Concurrent_collection_type)) {
- assert(_cmsGen->debug_collection_type() !=
- ConcurrentMarkSweepGeneration::Unknown_collection_type,
- "Bad cms collection type");
- return false;
- }
- )
- FreelistLocker x(this);
- // ------------------------------------------------------------------
- // Print out lots of information which affects the initiation of
- // a collection.
- if (PrintCMSInitiationStatistics && stats().valid()) {
- gclog_or_tty->print("CMSCollector shouldConcurrentCollect: ");
- gclog_or_tty->stamp();
- gclog_or_tty->print_cr("");
- stats().print_on(gclog_or_tty);
- gclog_or_tty->print_cr("time_until_cms_gen_full %3.7f",
- stats().time_until_cms_gen_full());
- gclog_or_tty->print_cr("free="SIZE_FORMAT, _cmsGen->free());
- gclog_or_tty->print_cr("contiguous_available="SIZE_FORMAT,
- _cmsGen->contiguous_available());
- gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate());
- gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate());
- gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy());
- gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy());
- gclog_or_tty->print_cr("metadata initialized %d",
- MetaspaceGC::should_concurrent_collect());
- }
- // ------------------------------------------------------------------
- // 第二种触发情况
- // If the estimated time to complete a cms collection (cms_duration())
- // is less than the estimated time remaining until the cms generation
- // is full, start a collection.
- if (!UseCMSInitiatingOccupancyOnly) {
- if (stats().valid()) {
- if (stats().time_until_cms_start() == 0.0) {
- return true;
- }
- } else {
- // We want to conservatively collect somewhat early in order
- // to try and "bootstrap" our CMS/promotion statistics;
- // this branch will not fire after the first successful CMS
- // collection because the stats should then be valid.
- if (_cmsGen->occupancy() >= _bootstrap_occupancy) {
- if (Verbose && PrintGCDetails) {
- gclog_or_tty->print_cr(
- " CMSCollector: collect for bootstrapping statistics:"
- " occupancy = %f, boot occupancy = %f", _cmsGen->occupancy(),
- _bootstrap_occupancy);
- }
- return true;
- }
- }
- }
- // 第三种触发情况
- // Otherwise, we start a collection cycle if
- // old gen want a collection cycle started. Each may use
- // an appropriate criterion for making this decision.
- // XXX We need to make sure that the gen expansion
- // criterion dovetails well with this. XXX NEED TO FIX THIS
- if (_cmsGen->should_concurrent_collect()) {
- if (Verbose && PrintGCDetails) {
- gclog_or_tty->print_cr("CMS old gen initiated");
- }
- return true;
- }
- // 第四种触发情况
- // We start a collection if we believe an incremental collection may fail;
- // this is not likely to be productive in practice because it's probably too
- // late anyway.
- GenCollectedHeap* gch = GenCollectedHeap::heap();
- assert(gch->collector_policy()->is_two_generation_policy(),
- "You may want to check the correctness of the following");
- if (gch->incremental_collection_will_fail(true /* consult_young */)) {
- if (Verbose && PrintGCDetails) {
- gclog_or_tty->print("CMSCollector: collect because incremental collection will fail ");
- }
- return true;
- }
- // 第五种触发情况
- if (MetaspaceGC::should_concurrent_collect()) {
- if (Verbose && PrintGCDetails) {
- gclog_or_tty->print("CMSCollector: collect for metadata allocation ");
- }
- return true;
- }
- return false;
- }
上述代码可知,从大类上分, background collector 一共有 5 种触发情况:
1.是否是并行 Full GC
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|