点击上方"涤生的博客",关注我
转载请注明原创出处,谢谢! 如果读完觉得有收获的话,欢迎点赞加关注。
前言
经常有同学会问,为啥我的应用 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-&