首先,根据 DisableExplicitGC 这个 JVM 启动参数的状态,确定是否会 GC,如果需要 GC,不同 GC 会有不同的处理。

1. G1 GC 的处理

如果是 System.gc() 触发的 GC,G1 GC 会根据 ExplicitGCInvokesConcurrent 这个 JVM 参数决定是默认 GC (轻量 GC,YoungGC)还是 FullGC。

参考代码g1CollectedHeap.cpp

//是否应该并行 GC,也就是较为轻量的 GC,对于 GCCause::_java_lang_system_gc,这里就是判断 ExplicitGCInvokesConcurrent 这个 JVM 是否为 true
if (should_do_concurrent_full_gc(cause)) {
return try_collect_concurrently(cause,
gc_count_before,
old_marking_started_before);
}// 省略其他这里我们不关心的判断分支
else {
//否则进入 full GC
VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause);
VMThread::execute(&op);
return op.gc_succeeded();
}

2. ZGC 的处理

直接不处理,不支持通过 System.gc() 触发 GC。

参考源码:zDriver.cpp

void ZDriver::collect(GCCause::Cause cause) {
switch (cause) {
//注意这里的 _wb 开头的 GC 原因,这代表是 WhiteBox 触发的,后面我们会用到,这里先记一下
case GCCause::_wb_young_gc:
case GCCause::_wb_conc_mark:
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_scavenge_alot:
case GCCause::_jvmti_force_gc:
case GCCause::_metadata_GC_clear_soft_refs:
// Start synchronous GC
_gc_cycle_port.send_sync(cause);
break; case GCCause::_z_timer:
case GCCause::_z_warmup:
case GCCause::_z_allocation_rate:
case GCCause::_z_allocation_stall:
case GCCause::_z_proactive:
case GCCause::_z_high_usage:
case GCCause::_metadata_GC_threshold:
// Start asynchronous GC
_gc_cycle_port.send_async(cause);
break; case GCCause::_gc_locker:
// Restart VM operation previously blocked by the GC locker
_gc_locker_port.signal();
break; case GCCause::_wb_breakpoint:
ZBreakpoint::start_gc();
_gc_cycle_port.send_async(cause);
break; //对于其他原因,不触发GC,GCCause::_java_lang_system_gc 会走到这里
default:
// Other causes not supported
fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
break;
}
}

3. Shenandoah GC 的处理

Shenandoah 的处理和 G1 GC 的类似,先判断是不是用户明确触发的 GC,然后通过 DisableExplicitGC 这个 JVM 参数判断是否可以 GC(其实这个是多余的,可以去掉,因为外层JVM_ENTRY_NO_ENV(void, JVM_GC(void))已经处理这个状态位了)。如果可以,则请求 GC,阻塞等待 GC 请求被处理。然后根据 ExplicitGCInvokesConcurrent 这个 JVM 参数决定是默认 GC (轻量并行 GC,YoungGC)还是 FullGC

参考源码shenandoahControlThread.cpp

void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
assert(GCCause::is_user_requested_gc(cause) ||
GCCause::is_serviceability_requested_gc(cause) ||
cause == GCCause::_metadata_GC_clear_soft_refs ||
cause == GCCause::_full_gc_alot ||
cause == GCCause::_wb_full_gc ||
cause == GCCause::_scavenge_alot,
"only requested GCs here");
//如果是显式GC(即如果是GCCause::_java_lang_system_gc,GCCause::_dcmd_gc_run,GCCause::_jvmti_force_gc,GCCause::_heap_inspection,GCCause::_heap_dump中的任何一个)
if (is_explicit_gc(cause)) {
//如果没有关闭显式GC,也就是 DisableExplicitGC 为 false
if (!DisableExplicitGC) {
//请求 GC
handle_requested_gc(cause);
}
} else {
handle_requested_gc(cause);
}
}

请求 GC 的代码流程是:

void ShenandoahControlThread::handle_requested_gc(GCCause::Cause cause) {
MonitorLocker ml(&_gc_waiters_lock);
//获取当前全局 GC id
size_t current_gc_id = get_gc_id();
//因为要进行 GC ,所以将id + 1
size_t required_gc_id = current_gc_id + 1;
//直到当前全局 GC id + 1 为止,代表 GC 执行了
while (current_gc_id < required_gc_id) {
//设置 gc 状态位,会有其他线程扫描执行 gc
_gc_requested.set();
//记录 gc 原因,根据不同原因有不同的处理策略,我们这里是 GCCause::_java_lang_system_gc
_requested_gc_cause = cause;
//等待 gc 锁对象 notify,代表 gc 被执行并完成
ml.wait();
current_gc_id = get_gc_id();
}
}

对于GCCause::_java_lang_system_gc,GC 的执行流程大概是:

bool explicit_gc_requested = _gc_requested.is_set() &&  is_explicit_gc(_requested_gc_cause);

//省略一些代码

else if (explicit_gc_requested) {
cause = _requested_gc_cause;
log_info(gc)("Trigger: Explicit GC request (%s)", GCCause::to_string(cause)); heuristics->record_requested_gc();
// 如果 JVM 参数 ExplicitGCInvokesConcurrent 为 true,则走默认轻量 GC
if (ExplicitGCInvokesConcurrent) {
policy->record_explicit_to_concurrent();
mode = default_mode;
// Unload and clean up everything
heap->set_unload_classes(heuristics->can_unload_classes());
} else {
//否则,执行 FullGC
policy->record_explicit_to_full();
mode = stw_full;
}
}

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

2021-2-28:调用 System.gc() 后究竟发生了什么?的更多相关文章

  1. (转)调用System.gc没有立即执行的解决方法

    调用System.gc没有立即执行的解决方法 查看源码 当我们调用System.gc()的时候,其实并不会马上进行垃圾回收,甚至不一定会执行垃圾回收,查看系统源码可以看到 /** * Indicate ...

  2. 在浏览器地址栏输入URL,按下回车后究竟发生了什么?

    1.DNS 在浏览器中输入URL后,首先要进行DNS解析,DNS解析的顺序为: 浏览器缓存 本地hosts文件 系统缓存 路由器缓存 DNS服务器迭代查询 2.发送请求 通过DNS得到目标的IP地址后 ...

  3. System.gc

    Java中的内存分配是随着new一个新的对象来实现的,这个很简单,而且也还是有一些可以“改进”内存回收的机制的,其中最显眼的就是这个System.gc()函数. 乍一看这个函数似乎是可以进行垃圾回收的 ...

  4. System.gc()和-XX:+DisableExplicitGC启动参数,以及DirectByteBuffer的内存释放

    首先我们修改下JVM的启动参数,重新运行之前博客中的代码.JVM启动参数和测试代码如下: -verbose:gc -XX:+PrintGCDetails -XX:+DisableExplicitGC ...

  5. JVM诊断及工具笔记(2)使用arthas定位哪里执行了System#gc()

    笔者是汽车之家实时计算平台的一名小伙伴.负责flink平台,数据湖及kafka平台的设计与开发.平时擅长做平台设计,定位及解决各种疑难杂症.第二篇文章,讲的点依旧很小,但是这次图多!!! 在这里感谢支 ...

  6. Java垃圾回收System.gc()的理解

    System.gc()无法保证GC一定执行 在默认情况下,通过System.gc()或者Runtime.getRuntime().gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收 ...

  7. JVM相关 - 深入理解 System.gc()

    本文基于 Java 17-ea,但是相关设计在 Java 11 之后是大致一样的 我们经常在面试中询问 System.gc() 究竟会不会立刻触发 Full GC,网上也有很多人给出了答案,但是这些答 ...

  8. Java的垃圾回收机制:强制回收System.gc() Runtime.getTime().gc()

    垃圾回收 当引用类型的实体,如对象.数组等不再被任何变量引用的时候.这块占用的内存就成为了垃圾.JVM会根据自己的策略决定是回收内存 注意: 垃圾回收只回收内存中的对象,无法回收物理资源(数据库连接, ...

  9. System.gc()与Object.finalize()的区别

    finalize()是由JVM自动调用的,你可以用System.gc(),但JVM不一定会立刻执行,JVM感觉内存空间有限时,才会开始执行finalize(),至于新的对象创建个数和被收集个数不同是因 ...

随机推荐

  1. allure生成的报告打开后显示loading

    allure生成的报告打开后显示loading,怎么办? 1. allure生成报告的命令 1.1.生成测试数据 # 命令格式:pytest 相关参数 指定执行的用例 --alluredir=数据存放 ...

  2. 使用VisualStudio直接运行简单的C#语句

    场景 经常有这样的需求, 想要测试一些简单的C#语法, 或者测试一下 文件 目录 操作相关的Api, 通常的做法是建立一个C#控制台项目, 然后写代码测试, 但是这样的做法对测试简单的语法和Api来说 ...

  3. OLAP、OLTP的介绍和比较(转载)

    OLTP与OLAP的介绍 数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing).联机分析处理OLAP(On-Line Analytical ...

  4. 组合数取模及Lucas定理

    引入: 组合数C(m,n)表示在m个不同的元素中取出n个元素(不要求有序),产生的方案数.定义式:C(m,n)=m!/(n!*(m-n)!)(并不会使用LaTex QAQ). 根据题目中对组合数的需要 ...

  5. Andrew算法(我确实不懂Graham)

    先解释一下:这两个算法分别都是凸包问题的算法,然后Andrew是Graham的变种,速度更快,更稳定,非常优秀,介于我已经把Graham写的莫名其妙的WA了,所以我选择了这种算法! 我认为在这里,还是 ...

  6. Codeforces Round #696 (Div. 2) C. Array Destruction (贪心,multiset)

    题意:有\(n\)个数,首先任选一个正整数\(x\),然后在数组中找到两个和为\(x\)的数,然后去掉这两个数,\(x\)更新为两个数中较大的那个.问你最后时候能把所有数都去掉,如果能,输出最初的\( ...

  7. linux 部署 .net core mvc

    1.本地编写一个mvc网站 代码编辑器:Visual studio 2017.2019.Visual Code 均可 1)搭建 略. (请自行搜索如何编辑mvc,或看文末参考链接) 2)配置 Prog ...

  8. Entity Framework (EF) Core学习笔记 1

    1. Entity Framework (EF) Core 是轻量化.可扩展.开源和跨平台的数据访问技术,它还是一 种对象关系映射器 (ORM),它使 .NET 开发人员能够使用面向对象的思想处理数据 ...

  9. Ubuntu——常用命令

    一.进入文件夹 1.cd .. # 进入上一个文件夹 2.cd ../.. # 进入上两个文件夹 3.cd - # 去到进入当前文件夹之前的那个文件夹 二.复制文件 cp 目标文件 复制文件名 三.移 ...

  10. Harbor 镜像仓库搭建

    安装 Docker # 下载 Docker 二进制包 [root@k8s-master01 ~]# wget https://download.docker.com/linux/static/stab ...