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

1. G1 GC 的处理

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

参考代码g1CollectedHeap.cpp

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

2. ZGC 的处理

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

参考源码:zDriver.cpp

  1. void ZDriver::collect(GCCause::Cause cause) {
  2. switch (cause) {
  3. //注意这里的 _wb 开头的 GC 原因,这代表是 WhiteBox 触发的,后面我们会用到,这里先记一下
  4. case GCCause::_wb_young_gc:
  5. case GCCause::_wb_conc_mark:
  6. case GCCause::_wb_full_gc:
  7. case GCCause::_dcmd_gc_run:
  8. case GCCause::_java_lang_system_gc:
  9. case GCCause::_full_gc_alot:
  10. case GCCause::_scavenge_alot:
  11. case GCCause::_jvmti_force_gc:
  12. case GCCause::_metadata_GC_clear_soft_refs:
  13. // Start synchronous GC
  14. _gc_cycle_port.send_sync(cause);
  15. break;
  16. case GCCause::_z_timer:
  17. case GCCause::_z_warmup:
  18. case GCCause::_z_allocation_rate:
  19. case GCCause::_z_allocation_stall:
  20. case GCCause::_z_proactive:
  21. case GCCause::_z_high_usage:
  22. case GCCause::_metadata_GC_threshold:
  23. // Start asynchronous GC
  24. _gc_cycle_port.send_async(cause);
  25. break;
  26. case GCCause::_gc_locker:
  27. // Restart VM operation previously blocked by the GC locker
  28. _gc_locker_port.signal();
  29. break;
  30. case GCCause::_wb_breakpoint:
  31. ZBreakpoint::start_gc();
  32. _gc_cycle_port.send_async(cause);
  33. break;
  34. //对于其他原因,不触发GC,GCCause::_java_lang_system_gc 会走到这里
  35. default:
  36. // Other causes not supported
  37. fatal("Unsupported GC cause (%s)", GCCause::to_string(cause));
  38. break;
  39. }
  40. }

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

  1. void ShenandoahControlThread::request_gc(GCCause::Cause cause) {
  2. assert(GCCause::is_user_requested_gc(cause) ||
  3. GCCause::is_serviceability_requested_gc(cause) ||
  4. cause == GCCause::_metadata_GC_clear_soft_refs ||
  5. cause == GCCause::_full_gc_alot ||
  6. cause == GCCause::_wb_full_gc ||
  7. cause == GCCause::_scavenge_alot,
  8. "only requested GCs here");
  9. //如果是显式GC(即如果是GCCause::_java_lang_system_gc,GCCause::_dcmd_gc_run,GCCause::_jvmti_force_gc,GCCause::_heap_inspection,GCCause::_heap_dump中的任何一个)
  10. if (is_explicit_gc(cause)) {
  11. //如果没有关闭显式GC,也就是 DisableExplicitGC 为 false
  12. if (!DisableExplicitGC) {
  13. //请求 GC
  14. handle_requested_gc(cause);
  15. }
  16. } else {
  17. handle_requested_gc(cause);
  18. }
  19. }

请求 GC 的代码流程是:

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

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

  1. bool explicit_gc_requested = _gc_requested.is_set() && is_explicit_gc(_requested_gc_cause);
  2. //省略一些代码
  3. else if (explicit_gc_requested) {
  4. cause = _requested_gc_cause;
  5. log_info(gc)("Trigger: Explicit GC request (%s)", GCCause::to_string(cause));
  6. heuristics->record_requested_gc();
  7. // 如果 JVM 参数 ExplicitGCInvokesConcurrent 为 true,则走默认轻量 GC
  8. if (ExplicitGCInvokesConcurrent) {
  9. policy->record_explicit_to_concurrent();
  10. mode = default_mode;
  11. // Unload and clean up everything
  12. heap->set_unload_classes(heuristics->can_unload_classes());
  13. } else {
  14. //否则,执行 FullGC
  15. policy->record_explicit_to_full();
  16. mode = stw_full;
  17. }
  18. }

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种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. 2019 第十届蓝桥杯大赛软件类省赛 Java A组 题解

    2019 第十届蓝桥杯大赛软件类省赛 Java A组 试题A 题解 ​ 题目最后一句贴心的提示选手应该使用 long (C/C++ 应该使用 long long). ​ 本题思路很直白,两重循环.外层 ...

  2. shell编程基础一

    1.定义变量 a=1 shell定义变量要注意等号前后不能有空格,不然会报错,请严格按照格式编写. 2.打印输出 echo 1 使用echo打印,后面留一个空格. 3.shell中通过 ${变量名} ...

  3. B - B Saruman's Army(贪心)

    在一条直线上,有n个点.从这n个点中选择若干个,给他们加上标记.对于每一个点,其距离为R以内的区域里必须有一个被标记的点.问至少要有多少点被加上标记 Saruman the White must le ...

  4. Codeforces Round #658 (Div. 2)【ABC2】

    做完前四题还有一个半小时... 比赛链接:https://codeforces.com/contest/1382 A. Common Subsequence 题意 给出两个数组,找出二者最短的公共子序 ...

  5. 2020牛客暑期多校训练营(第八场) Kabaleo Lite

    传送门:Kabaleo Lite 题意 有n道菜,1≤n≤105,a[i]是每道菜可以赚的钱,−109≤ ai ≤109,b[i]是这道菜的个数,1≤bi≤105,你每次只能选从1开始连续的菜,然后问 ...

  6. 2019 Multi-University Training Contest 5——permutation 2

    传送门 题意: t组输入,之后每组例子有三个数n.x.y代表在一个以x为开头y为结尾的长为n的数组里面,开头和结尾数据已经固定,让你从1--n中找其他数据填入数组中 (每个数据不能重复使用),使它满足 ...

  7. AtCoder Beginner Contest 179 D - Leaping Tak (DP)

    题意:给你一个数字\(n\)和\(k\)个区间,\(S\)表示所有区间的并的集合,你目前在\(1\),每次可以从集合中选择一个数字向右移动,问有多少种方法从\(1\)走到\(n\). 题解:我们从1开 ...

  8. Python——requests模块

    一.安装模块 pip install requests 二.引用 import requests 三.get方法 #GET访问页面 r = requests.get(url) print(r.text ...

  9. Linux-输出/输入重定向

    目录 重定向的分类 输出重定向 将标准输出重定向到文件 将标准输出追加重定向到文件 将错误输出重定向到文件 将标准输出和错误输出都重定向到文件 将错误输出重定向到黑洞文件 输入重定向 重定向的分类 名 ...

  10. after upgrade macOS Catalina bugs

    after upgrade macOS Catalina bugs 升级了macOS catalina后,碰到的 bugs? macOS 10.15.5 https://www.apple.com/m ...