JVM之HotSpot虚拟机是如何发起内存回收的?
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虚拟机是如何发起内存回收的?的更多相关文章
- JVM之HotSpot虚拟机是如何发起内存回收的? 转载
1.背景 在上一节中,我们掌握了垃圾收集的一些算法,也弄明白了分代回收的原理, 那么HotSpot虚拟机是如何发起内存回收的? 2.如何找到GC Roots根节点(枚举根节点) 从可达性分析中GC R ...
- 深入理解JVM:HotSpot虚拟机对象探秘
对象的创建 java是一门面向对象的语言.在Java程序执行过程中无时无刻有Java对象被创建出来.在语言层面上,创建对象(克隆.反序列化)一般是一个newkeyword而已,而在虚拟机中,对象的创建 ...
- 深入理解JVM(③)——之HotSpot虚拟机对象探秘
前言 上篇文章介绍了Java虚拟机的运行时数据区域,大致明白了Java虚拟机内存模型的概况,下面就基于实用优先的原则,以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,升入探讨一下Hot ...
- JVM:Hotspot虚拟机中的对象
在HotSpot虚拟机中,对象在内存中存储的布局可以被分为3个区域:对象头(Header).实例数据(Instance data)和对齐填充(Padding).对象头包括两部分信息,第一部分存储自身的 ...
- Java基础-JVM内存回收
Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:把对象分为年青代(Young).年老代(Tenured).持久代(Perm),对不同生命周期的对象使用不同的算法.( ...
- 虚拟机--第二章java内存区域与内存溢出异常--(抄书)
这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...
- Java内存回收 - 落日之心的日志 - 网易博客
body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...
- JVM系列之七:HotSpot 虚拟机
1. 对象的创建 1. 遇到 new 指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载.解析和初始化过.如果没有,执行相应的类加载. 2 ...
- JDK1.8-Java虚拟机运行时数据区域和HotSpot虚拟机的内存模型
目录 介绍 官方文档规定的运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈溢出 Java堆 演示堆内存溢出 方法区 运行时常量池 演示方法区溢出 HotSpot虚拟机的内 ...
随机推荐
- [基础-001]C++字符串转换(char*,const char*,string)
1. string转const char* string str ="abc"; const char* charArr = str.c_str(); 2. const char* ...
- XMLHttpRequest实现Ajax &数据格式JSON
GET请求 index <%@ page language="java" contentType="text/html; charset=utf-8" p ...
- css 3 背景图片为渐变色(渐变色背景图片) 学习笔记
6年不研究CSS发现很多现功能都没有用过,例如渐变色,弹性盒子等,年前做过一个简单的管理系统,由于本人美工不好,设计不出好看的背景图片,偶然百度到背景图片可以使用渐变色(感觉发现了新大陆).以后的项目 ...
- Rocket - util - LanePositionedQueue
https://mp.weixin.qq.com/s/yO_9Ec3S5-AosRVLpsBgOg 简单介绍基于通道位置的队列(LanePositionedQueue)的实现. 1. ...
- Rocket - diplomacy - DUEB参数模型分析
https://mp.weixin.qq.com/s/533bJxcPRgO4W2gf_OEhEw 分析DUEB参数模型中各种参数类型的可能性. 1. 节点类型 根据参数的传播方向,可 ...
- Javascript中target事件属性,事件的目标节点的获取。
window.event.srcElement与window.event.target 都是指向触发事件的元素,它是什么就有什么样的属性 srcElement是事件初始化目标html元素对象引用,因为 ...
- Java实现 LeetCode 747 至少是其他数字两倍的最大数(暴力)
747. 至少是其他数字两倍的最大数 在一个给定的数组nums中,总是存在一个最大元素 . 查找数组中的最大元素是否至少是数组中每个其他数字的两倍. 如果是,则返回最大元素的索引,否则返回-1. 示例 ...
- Java实现 蓝桥杯 算法训练 2的次幂表示
算法训练 2的次幂表示 时间限制:1.0s 内存限制:512.0MB 问题描述 任何一个正整数都可以用2进制表示,例如:137的2进制表示为10001001. 将这种2进制表示写成2的次幂的和的形式, ...
- Java实现 LeetCode 174 地下城游戏
174. 地下城游戏 一些恶魔抓住了公主(P)并将她关在了地下城的右下角.地下城是由 M x N 个房间组成的二维网格.我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来 ...
- java实现第四届蓝桥杯振兴中华
振兴中华 题目描述 小明参加了学校的趣味运动会,其中的一个项目是:跳格子. 地上画着一些格子,每个格子里写一个字,如下所示:(也可参见p1.jpg) 从我做起振 我做起振兴 做起振兴中 起振兴中华 比 ...