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. Win10下安装Linux子系统-Ubuntu

    工作以来一直DotNet系偏C/S, 接触Web开发的时间也不长, 现在主要偏向Web全栈方向, 一直对Linux系统心生向往, 夜深了娃睡了, 打开老旧的笔记本来折腾一下. 准备工作 控制面板 &g ...

  2. [PHP自动化-进阶]002.CURL模拟登录带有验证码的网站

    引言:继前文<模拟登录并采集数据>,大家似乎看不过瘾,这会再出一发,模拟实现带验证码网站的登录. 这篇文章主要介绍了PHP使用CURL实现对带有验证码的网站进行模拟登录的方法,可以帮助读者 ...

  3. Java中的集合(十二) 实现Map接口的WeakHashMap

    Java中的集合(十二) 实现Map接口的WeakHashMap 一.WeakHashMap简介 WeakHashMap和HashMap一样,WeakHashMap也是一个哈希表,存储的也是键值对(k ...

  4. 关于CAS单点登录配置文件误修改导致的JSP报错问题

    weblogic环境下搭建CXF环境时,为了避免CAS拦截webservice服务URL所以设置CAS为不拦截,无修改了CAS配置的其它地方导致工程启动正常,一旦访问就会报JSP的错误. [Servl ...

  5. Rocket - tilelink - WidthWidget

    https://mp.weixin.qq.com/s/pmJcsRMviJZjMwlwYw6OgA   简单介绍WidthWidget的实现.   ​​   1. 基本介绍   用于设定与上游节点连接 ...

  6. Rocket - diplomacy - 模块结构信息

    https://mp.weixin.qq.com/s/cTRxXwWNEeb4-XX_t4bRcg   讨论模块结构信息的来源及使用方式.     ​​   1. diplomacy   diplom ...

  7. undefined attribute name (XXXX)

    Window --> Preferences --> Web --> HTML Files --> Editor --> Validation --> Attrib ...

  8. Java实现 LeetCode 641 设计循环双端队列(暴力)

    641. 设计循环双端队列 设计实现双端队列. 你的实现需要支持以下操作: MyCircularDeque(k):构造函数,双端队列的大小为k. insertFront():将一个元素添加到双端队列头 ...

  9. Java实现 LeetCode 406 根据身高重建队列

    406. 根据身高重建队列 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. ...

  10. Java实现 蓝桥杯VIP 算法提高 不同单词个数统计

    算法提高 不同单词个数统计 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,输入一个句子,然后统计出这个句子当中不同的单词个数.例如:对于句子"one little t ...