首先,根据 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. c++nullptr(空指针常量)、constexpr(常量表达式)

    总述     又来更新了,今天带来的是nullptr空指针常量.constexpr(常量表达式)C++的两个用法.Result result_fun = nullptr;constexpr stati ...

  2. 四十五:漏洞发现-API接口服务之漏洞探针类型利用修复

    接口服务类安全测试 根据前期信息收集针对目标端口服务类探针后进行的安全测试,主要涉及攻击方法:口令安全,WEB类漏洞,版本漏洞等,其中产生的危害可大可小,属于端口服务/第三方服务类安全测试.一般在已知 ...

  3. 大整数四则运算(vector与数组两种版本实现)

    每逢大整数四则运算,都会怯懦,虽是算法竞赛必会的东西,也零散的学过,简单的总结过,但不成体系的东西心里一直没底. 所以今天消耗了大量的卡路里,啃了几套模板之后终于总结成了一套自己的模板 再也不用担心大 ...

  4. HDU5740 Glorious Brilliance【最短路 KM匹配】

    HDU5740 Glorious Brilliance 题意: 给出一张不一定合法的染色图,每次可以交换相邻两点的颜色,问最少多少次能使染色图合法 合法的染色图相邻点的颜色不能相同 题解: 首先要确定 ...

  5. MySQL 连接管理

    目录 MySQL 连接方式 TCP/IP 连接 Socket 连接 MySQL 连接工具 自带连接工具 第三方连接工具 MySQL 连接方式 TCP/IP 连接 # TCP/IP 连接 mysql - ...

  6. Linux-文件查看命令

    目录 系统文件查看命令-cat 系统文件查看命令-more 系统文件查看命令-less 系统文件查看命令-head 系统文件查看命令-tail 系统文件查看命令-grep 文件上传下载命令-rz,sz ...

  7. LINUX - 通信

    为什么三次握手: 让服务端和客户端都知道,自己的收信能力和发信能力没有问题: 第一次:客户端发给服务端--服务端知道了,自己的收信能力和客户端的发信能力没有问题: 第二次:服务端回复客户端--客户端知 ...

  8. Leetcode(18)-四数之和

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满 ...

  9. spring-cloud-netflix-hystrix-dashboard

    Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数 ...

  10. Semantic Pull Requests All In One

    Semantic Pull Requests All In One https://github.com/zeke/semantic-pull-requests docs: Update direct ...