1.背景

在上一节中,我们掌握了垃圾收集的一些算法,也弄明白了分代回收的原理,

那么HotSpot虚拟机是如何发起内存回收的?

2.如何找到GC Roots根节点(枚举根节点)

  从可达性分析中GC Roots 节点找引用链这个操作为例,可作为GC Roots 的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中,现在的很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,那么必然会消耗很多时间。

  另外,可达性分析对执行时间的敏感还体现在GC停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行—这里的一致性的意思是指在整个分析期间整个执行系统看起来像被冻结在某个时间点上,不可以出现在分析过程中对象引用关系还在不断的变化,该点不满足的话分析结果的准确性就无法得到保证。这点导致GC进行时必须停顿所有Java执行线程(Sun称这件事情为“Stop The World”)的其中一个重要的原因,即使在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点时也是必须要停顿的。

  目前主流的Java虚拟机使用的都是准确式GC,所以当执行系统停顿下来后,并不需要一个不漏的检查完所有的执行上下文和全局的引用位置,虚拟机应当是有办法直接得知哪些地方存在着对象引用。在HotSpot的实现中,是使用一组称为OopMap的数据结构来达到这个目的的,在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样,GC在扫描时就可以直接得知这些信息了。

总结:

3.安全点

3.1.什么是安全点,为什么需要安全点

在 JVM 中如何判断对象可以被回收 一文中,我们知道 HotSpot 虚拟机采取的是可达性分析算法。即通过 GC Roots 枚举判定待回收的对象。

那么,首先要找到哪些是 GC Roots。

有两种查找 GC Roots 的方法:

一种是遍历方法区和栈区查找(保守式 GC)-成本高。

一种是通过 OopMap 数据结构来记录 GC Roots 的位置(准确式 GC)-可以快速定位。

很明显,保守式 GC 的成本太高。

准确式 GC 的优点就是能够让虚拟机快速定位到 GC Roots。

对应 OopMap 的位置即可作为一个安全点(Safe Point),因为不能为每一条指令都生成对应的OopM,那将会需要大量的额外空间

在执行 GC 操作时,所有的工作线程必须停顿,这就是所谓的”Stop-The-World”。

为什么呢?

因为可达性分析算法必须是在一个确保一致性的内存快照中进行。如果在分析的过程中对象引用关系还在不断变化,分析结果的准确性就不能保证。

安全点意味着在这个点时,所有工作线程的状态是确定的,JVM 就可以安全地执行 GC 。

3.2.如何选择安全点

安全点太多,GC 过于频繁,增大运行时负荷;

安全点太少,GC 等待时间太长。

一般会在如下几个位置选择安全点:

1、循环的末尾

2、方法临返回前

3、调用方法之后

4、抛异常的位置

3.3.为什么选择以上的位置作为安全点

主要的目的就是避免程序长时间无法进入 Safe Point。

比如 JVM 在做 GC 之前要等所有的应用线程进入安全点,

如果有一个线程一直没有进入安全点,就会导致 GC 时 JVM 停顿时间延长。

比如这里,超大的循环导致执行 GC 等待时间过长。

3.4.如何在GC发生时,所有线程都跑到最近安全点上

主要有两种方式:

抢断式中断:在 GC 发生时,首先中断所有线程,如果发现线程未执行到 Safe Point,就恢复线程让其运行到 Safe Point 上。

主动式中断:在 GC 发生时,不直接操作线程中断,而是简单地设置一个标志,让各个线程执行时主动轮询这个标志,发现中断标志为真时就自己中断挂起。

JVM 采取的就是主动式中断。轮询标志的地方和安全点是重合的。

4.安全区域

4.1.为什么需要安全域

上面讲述的Safepoint似乎已经完美的解决了如何进入GC的问题,但实际情况却不一定。

Safepoint机制保证了程序执行时,在不长的时间内就会遇到可进入GC的Safepoint。

但是,程序不执行的时候呢?所谓的程序不执行就是没有分配CPU时间,典型的例子就是线程处于sleep状态或者Blocked状态,这时候线程无法响应JVM的中断请求,“走”到安全的地方去中断挂起,

JVM显然也不太可能等待线程重新被分配CPU时间。这种情况,就需要安全区域来解决了。

4.2.什么是安全域

Safe Region 是指在一段代码片段中,引用关系不会发生变化(如sleep的时候)。在这个区域内的任意地方开始 GC 都是安全的。

4.3.安全域中如何发起GC

线程在进入 Safe Region 的时候先标记自己已进入了 Safe Region,

等到被唤醒时准备离开 Safe Region 时,先检查能否离开,

如果 GC 完成了,那么线程可以离开,

否则它必须等待直到收到安全离开的信号为止。

5.综合总结

假设HotSpot需要发生GC,首先需要找所有的GC Roots根节点,

但是在内存中要想找到所有的根节点,并检查引用链,必然会消耗很多时间,HotSpot肯定不会去扫描整个内存去找根节点;

HotSpot是使用一组oopMap数据结构来存放对象的引用,这样就可以快速准确的完成GC Roots的查找;

但是,问题又来了,如果每一条指令都生成对应的oopMap将会需要大量的额外空间;

那怎么办呢,我们可以只在需要垃圾回收的点上记录(生成oopMap)这些信息,这个点我们就叫这安全点,要发送GC时,等所有的线程都运行到这个点上再回收;

但是问题又来了,如果有的线程处于sleep或者Blocked状态呢,这时候这些线程无法响应JVM的中断请求,到达安全点;

这样这种情况那就是安全域来解决,在安全域中引用关系不会发生变化,所以只要在安全域中就可以直接回收,不需要到达安全点;

完美!

参考:《深入理解Java虚拟机》 周志明 编著:

JVM之HotSpot虚拟机是如何发起内存回收的?的更多相关文章

  1. JVM之HotSpot虚拟机是如何发起内存回收的? 转载

    1.背景 在上一节中,我们掌握了垃圾收集的一些算法,也弄明白了分代回收的原理, 那么HotSpot虚拟机是如何发起内存回收的? 2.如何找到GC Roots根节点(枚举根节点) 从可达性分析中GC R ...

  2. 深入理解JVM:HotSpot虚拟机对象探秘

    对象的创建 java是一门面向对象的语言.在Java程序执行过程中无时无刻有Java对象被创建出来.在语言层面上,创建对象(克隆.反序列化)一般是一个newkeyword而已,而在虚拟机中,对象的创建 ...

  3. 深入理解JVM(③)——之HotSpot虚拟机对象探秘

    前言 上篇文章介绍了Java虚拟机的运行时数据区域,大致明白了Java虚拟机内存模型的概况,下面就基于实用优先的原则,以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,升入探讨一下Hot ...

  4. JVM:Hotspot虚拟机中的对象

    在HotSpot虚拟机中,对象在内存中存储的布局可以被分为3个区域:对象头(Header).实例数据(Instance data)和对齐填充(Padding).对象头包括两部分信息,第一部分存储自身的 ...

  5. Java基础-JVM内存回收

    Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:把对象分为年青代(Young).年老代(Tenured).持久代(Perm),对不同生命周期的对象使用不同的算法.( ...

  6. 虚拟机--第二章java内存区域与内存溢出异常--(抄书)

    这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...

  7. Java内存回收 - 落日之心的日志 - 网易博客

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  8. JVM系列之七:HotSpot 虚拟机

    1. 对象的创建 1. 遇到 new 指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载.解析和初始化过.如果没有,执行相应的类加载. 2 ...

  9. JDK1.8-Java虚拟机运行时数据区域和HotSpot虚拟机的内存模型

    目录 介绍 官方文档规定的运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈溢出 Java堆 演示堆内存溢出 方法区 运行时常量池 演示方法区溢出 HotSpot虚拟机的内 ...

随机推荐

  1. 【C++】赋值过程中类型转换

    注意:以下内容摘自文献[1],修改了部分内容. 1.赋值过程中的类型转换 如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时自动进行类型转换. (1) 将浮点型数据(包括单.双精度)赋给 ...

  2. [PHP学习教程 - 日期/时间]001.月份第一天&最后一天(Month First Day & Last Day)

    引言:在时间处理上,对于前/后台人性化的展示当前月份最大天数,这个是网站必须要处理的一个方面.但通常这一块会由第三方类库直接包装,这里我们做一个简单的Mark. 今天,我们就为大家提供一个函数,获得指 ...

  3. DataFrame-选择与切片

    取得DataFrame对象reviews的description列的前10个值(或者说reviews前10行的description列): reviews.iloc[:10].loc[:,'descr ...

  4. AUTOSAR-软件规范文档中的UML

    https://mp.weixin.qq.com/s/vm5vWNSpbNIYh25-LjJfYg   AUTOSAR软件规范文档中存在两种UML图: Sequence diagrams Config ...

  5. Java实现 蓝桥杯VIP 算法训练 完数

    问题描述 一个数如果恰好等于它的因子之和,这个数就称为"完数".例如,6的因子为1.2.3,而6=1+2+3,因此6就是"完数".又如,28的因子为1.2.4. ...

  6. Java实现 LeetCode 322 零钱兑换

    322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...

  7. Java实现 LeetCode 258 各位相加

    258. 各位相加 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数. 示例: 输入: 38 输出: 2 解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2. 由 ...

  8. Java实现 蓝桥杯VIP 算法提高 3000米排名预测

    算法提高 3000米排名预测 时间限制:1.0s 内存限制:256.0MB 问题描述 3000米长跑时,围观党们兴高采烈地预测着最后的排名.因为他们来自不同的班,对所有运动员不一定都了解,于是他们分别 ...

  9. java实现滑动解锁

    滑动解锁是智能手机一项常用的功能.你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的"相邻"的点.这些划过的点所组成的有向折线,如果与预设的折线在图案.方向上都一致 ...

  10. Java实现第九届蓝桥杯倍数问题

    倍数问题 题目描述 [题目描述] 众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数.但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼.现在小葱给了你 n 个数,希望你从这 n ...