我的编程空间,编程开发者的网络收藏夹
学习永远不晚

jvm原理之SystemGC源码分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

jvm原理之SystemGC源码分析

概述

JVM的GC一般情况下是JVM本身根据一定的条件触发的,不过我们还是可以做一些人为的触发,比如通过jvmti做强制GC,通过System.gc触发,还可以通过jmap来触发等,针对每个场景其实我们都可以写篇文章来做一个介绍,本文重点介绍下System.gc的原理

或许大家已经知道如下相关的知识

  • system.gc其实是做一次full gc
  • system.gc会暂停整个进程
  • system.gc一般情况下我们要禁掉,使用-XX:+DisableExplicitGC
  • system.gc在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent来做一次稍微高效点的GC(效果比Full GC要好些)
  • system.gc最常见的场景是RMI/NIO下的堆外内存分配等

如果你已经知道上面这些了其实也说明你对System.gc有过一定的了解,至少踩过一些坑,但是你是否更深层次地了解过它,比如

  • 为什么CMS GC下-XX:+ExplicitGCInvokesConcurrent这个参数加了之后会比真正的Full GC好?
  • 它如何做到暂停整个进程?
  • 堆外内存分配为什么有时候要配合System.gc?

如果你上面这些疑惑也都知道,那说明你很懂System.gc了,那么接下来的文字你可以不用看啦

JDK里的System.gc的实现

先贴段代码吧(java.lang.System)


public static void gc() {
    Runtime.getRuntime().gc();
}

发现主要调用的是Runtime里的gc方法(java.lang.Runtime)


public native void gc();

这里看到gc方法是native的,在java层面只能到此结束了,代码只有这么多,要了解更多,可以看方法上面的注释,不过我们需要更深层次地来了解其实现,那还是准备好进入到jvm里去看看

Hotspot里System.gc的实现

如何找到native里的实现

上面提到了Runtime.gc是一个本地方法,那需要先在jvm里找到对应的实现,这里稍微提一下jvm里native方法最常见的也是最简单的查找,jdk里一般含有native方法的类,一般都会有一个对应的c文件,比如上面的java.lang.Runtime这个类,会有一个Runtime.c的文件和它对应,native方法的具体实现都在里面了,如果你有source,可能会猜到和下面的方法对应

JNIEXPORT void JNICALL
Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
{
    JVM_GC();
}

其实没错的,就是这个方法,jvm要查找到这个native方法其实很简单的,看方法名可能也猜到规则了,Java_pkgName_className_methodName,其中pkgName里的".“替换成”_“,这样就能找到了,当然规则不仅仅只有这么一个,还有其他的,这里不细说了,有机会写篇文章详细介绍下其中细节

DisableExplicitGC参数

上面的方法里是调用JVM_GC(),实现如下

JVM_ENTRY_NO_ENV(void, JVM_GC(void))
  JVMWrapper("JVM_GC");
  if (!DisableExplicitGC) {
    Universe::heap()->collect(GCCause::_java_lang_system_gc);
  }
JVM_END

看到这里我们已经解释其中一个疑惑了,就是DisableExplicitGC这个参数是在哪里生效的,起的什么作用,如果这个参数设置为true的话,那么将直接跳过下面的逻辑,我们通过-XX:+ DisableExplicitGC就是将这个属性设置为true,而这个属性默认情况下是true还是false呢

product(bool, DisableExplicitGC, false,                                   
          "Tells whether calling System.gc() does a full GC")    

ExplicitGCInvokesConcurrent参数

这里主要针对CMSGC下来做分析,所以我们上面看到调用了heap的collect方法,我们找到对应的逻辑

void GenCollectedHeap::collect(GCCause::Cause cause) {
  if (should_do_concurrent_full_gc(cause)) {
#ifndef SERIALGC
    // mostly concurrent full collection
    collect_mostly_concurrent(cause);
#else  // SERIALGC
    ShouldNotReachHere();
#endif // SERIALGC
  } else {
#ifdef ASSERT
    if (cause == GCCause::_scavenge_alot) {
      // minor collection only
      collect(cause, 0);
    } else {
      // Stop-the-world full collection
      collect(cause, n_gens() - 1);
    }
#else
    // Stop-the-world full collection
    collect(cause, n_gens() - 1);
#endif
  }
}

bool GenCollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) {
  return UseConcMarkSweepGC &&
         ((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) ||
          (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent));
}

collect里一开头就有个判断,如果should_do_concurrent_full_gc返回true,那会执行collect_mostly_concurrent做并行的回收

其中should_do_concurrent_full_gc中的逻辑是如果使用CMS GC,并且是system gc且ExplicitGCInvokesConcurrent==true,那就做并行full gc,当我们设置-XX:+ ExplicitGCInvokesConcurrent的时候,就意味着应该做并行Full GC了,不过要注意千万不要设置-XX:+DisableExplicitGC,不然走不到这个逻辑里来了

并行Full GC相对正常的Full GC效率高在哪里

stop the world

说到GC,这里要先提到VMThread,在jvm里有这么一个线程不断轮询它的队列,这个队列里主要是存一些VM_operation的动作,比如最常见的就是内存分配失败要求做GC操作的请求等,在对gc这些操作执行的时候会先将其他业务线程都进入到安全点,也就是这些线程从此不再执行任何字节码指令,只有当出了安全点的时候才让他们继续执行原来的指令,因此这其实就是我们说的stop the world(STW),整个进程相当于静止了

CMS GC

这里必须提到CMS GC,因为这是解释并行Full GC和正常Full GC的关键所在,CMS GC我们分为两种模式background和foreground,其中background顾名思义是在后台做的,也就是可以不影响正常的业务线程跑,触发条件比如说old的内存占比超过多少的时候就可能触发一次background式的cms gc,这个过程会经历CMS GC的所有阶段,该暂停的暂停,该并行的并行,效率相对来说还比较高,毕竟有和业务线程并行的gc阶段;而foreground则不然,它发生的场景比如业务线程请求分配内存,但是内存不够了,于是可能触发一次cms gc,这个过程就必须是要等内存分配到了线程才能继续往下面走的,因此整个过程必须是STW的,因此CMS GC整个过程都是暂停应用的,但是为了提高效率,它并不是每个阶段都会走的,只走其中一些阶段,这些省下来的阶段主要是并行阶段,Precleaning、AbortablePreclean,Resizing这几个阶段都不会经历,其中sweep阶段是同步的,但不管怎么说如果走了类似foreground的cms gc,那么整个过程业务线程都是不可用的,效率会影响挺大。CMS GC具体的过程后面再写文章详细说,其过程确实非常复杂的

正常的Full GC

正常的Full GC其实是整个gc过程包括ygc和cms gc(这里说的是真正意义上的Full GC,还有些场景虽然调用Full GC的接口,但是并不会都做,有些时候只做ygc,有些时候只做cms gc)都是由VMThread来执行的,因此整个时间是ygc+cms gc的时间之和,其中CMS GC是上面提到的foreground式的,因此整个过程会比较长,也是我们要避免的

并行的Full GC

并行Full GC也通样会做YGC和CMS GC,但是效率高就搞在CMS GC是走的background的,整个暂停的过程主要是YGC+CMS_initMark+CMS_remark几个阶段

堆外内存常配合使用System GC

这里说的堆外内存主要针对java.nio.DirectByteBuffer,这些对象的创建过程会通过Unsafe接口直接通过os::malloc来分配内存,然后将内存的起始地址和大小存到java.nio.DirectByteBuffer对象里,这样就可以直接操作这些内存。这些内存只有在DirectByteBuffer回收掉之后才有机会被回收,因此如果这些对象大部分都移到了old,但是一直没有触发CMS GC或者Full GC,那么悲剧将会发生,因为你的物理内存被他们耗尽了,因此为了避免这种悲剧的发生,通过-XX:MaxDirectMemorySize来指定最大的堆外内存大小,当使用达到了阈值的时候将调用System.gc来做一次full gc,以此来回收掉没有被使用的堆外内存。

具体堆外内存是如何回收的,其原理机制又是怎样的,后面文章会详细写出,请大家持续关注编程网~

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

jvm原理之SystemGC源码分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么分析JVM原理

本篇文章为大家展示了怎么分析JVM原理,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1:什么是JVMJVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备
2023-06-02

Webpack学习之动态import原理及源码分析

这篇文章主要为大家介绍了Webpack学习之动态import原理及源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-17

vue3源码分析reactivity实现原理

这篇文章主要为大家介绍了vue3源码分析reactivity实现原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-28

vue原理Compile之optimize标记静态节点源码分析

这篇文章主要介绍“vue原理Compile之optimize标记静态节点源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vue原理Compile之optimize标记静态节点源码分析”文章能帮
2023-07-02

OpenMP task construct实现原理源码分析

本篇内容主要讲解“OpenMP task construct实现原理源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“OpenMP task construct实现原理源码分析”吧!从编译器
2023-07-05

Redis之quicklist源码分析

一、quicklist简介Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。其底层实现所依赖的内部数据
Redis之quicklist源码分析
2019-01-23

python虚拟机之描述器实现原理与源码分析

在本篇文章当中主要给大家介绍描述器背后的实现原理,通过分析 cpython对应的源代码了解与描述器相关的字节码的指令,我们就可以真正了解到描述器背后的原理,需要的朋友可以参考下
2023-05-19

ReactContext原理深入理解源码示例分析

这篇文章主要为大家介绍了ReactContext原理深入理解源码示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-03

SpringMVC源码分析6:SpringMVC的视图解析原理

转自 SpringMVC视图机制详解[附带源码分析]
2023-06-02

源码分析Java中ThreadPoolExecutor的底层原理

这篇文章主要带大家从源码分析一下Java中ThreadPoolExecutor的底层原理,文中的示例代码讲解详细,具有一定的学习价值,需要的可以参考一下
2023-05-19

Golang源码分析之golang/sync之singleflight

golang/sync库拓展了官方自带的sync库,提供了errgroup、semaphore、singleflight及syncmap四个包,本次先分析第一个包errgroup的源代码,下面这篇文章主要给大家介绍了关于Golang源码分析之golang/sync之singleflight的相关资料,需要的朋友可以参考下
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录