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. Java虚拟机垃圾回收(二) :垃圾回收算法(转载)

    1.标记-清除算法 标记-清除(Mark-Sweep)算法是一种基础的收集算法. 1.算法思路 "标记-清除"算法,分为两个阶段: (A).标记 首先标记出所有需要回收的对象: 标 ...

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

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

随机推荐

  1. BUUCTF-假如给我三天光明

    假如给我三天光明 打开压缩包可以看到一个海报,下方有盲文显示,通过对照表得知 盲文翻译为kmdonowg 通过盲文翻译得到的字符串解压压缩包得到一个音频文件 使用Audacity打开,看样子应该是摩斯 ...

  2. SAP -熟练使用T-Code SHD0

    SHD0 业务顾问和开发顾问都非常熟悉的一个T-Code, 如果能合理使用它,可以省去许多增强和程序修改工作. 当我需要时,我在这里找不到任何相关文档,这就是为什么我想借此机会向我们自己的SCN提供内 ...

  3. RPA应用场景-自动轮询汇总报表

    场景概述 自动轮询汇总报表 所涉系统名称 券商披露网站 人工操作(时间/次) 36小时 所涉人工数量 1 操作频率 每月 场景流程 1.每月初机器人自动登录网站轮询36家券商披露的财务报告,并下载 2 ...

  4. bat-Office激活命令

    激活命令 cd C:\Program Files\Microsoft Office\Office16 //然后目录对的话,该目录下面应该有个 OSPP.VBS cscript ospp.vbs /ds ...

  5. windows10 程序和功能没有Hyper-V选项

    1.在电脑桌面新建Hyper-V.cmd文件,将如下代码添加到文件中 pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*H ...

  6. LM431精密+3.3V产生电路

    精密+3.3V电压通过三段可调并联稳压器LM431电路产生.LM431稳压电路如下图所示. 输出电压 UO仅与电阻 R35.R38 有关,计算公式如下: 式中常数2.5为内部基准电压,其保持恒定不变. ...

  7. Java8 Stream 的最佳实践

    Java8 Stream 的最佳实践 java8stream提供了对于集合类的流失处理,其具有以下特点: Lazy Evaluation(长度可以无限) 只能使用一次 内部迭代 Lazy Evalua ...

  8. nexus org.sonatype.nexus.bootstrap.jetty.JettyServer - Start failed

    INFO [jetty-main-1] *SYSTEM org.sonatype.nexus.bootstrap.jetty.JettyServer - Runningjvm 1 | 2020-04- ...

  9. javaweb 01: servlet前言

    动力节点杜老师,javaweb最新课程的笔记,假期一直跟着bilibili上的课程在学,放在这里,方便复习 Servlet前言 关于系统架构 系统架构包括什么形式? C/S架构 B/S架构 C/S架构 ...

  10. 【RocketMQ】消息的消费

    上一讲[RocketMQ]消息的拉取 消息消费 当RocketMQ进行消息消费的时候,是通过ConsumeMessageConcurrentlyService的submitConsumeRequest ...