JVM垃圾收集之可达性分析
Posted Java从入坑到入土
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾收集之可达性分析相关的知识,希望对你有一定的参考价值。
JVM垃圾收集之可达性分析
JVM进行垃圾回收时会判断对象是否可以回收,我们需要对其进行概念上了解哪些对象是“垃圾”、JVM进入GC阶段时如何保证回收时的安全性和高效性。
是否回收对象的判断依据
JVM中主要有两种算法判断对象是否可回收,
第二种算法是重点
引用计数算法
给对象添加一个引用计数器,每当一个地方引用它时,计数器+1;当一个地方不再引用它时,计数器-1;直至计数器为0时,代表没有任何一个地方引用此对象,则判断对象“已死”。
JVM并没有引用这种算法,因为它存在对象之间
相互循环引用
的弊端
可达性分析算法
以一系列的
GC Roots
为起点开始向下搜索,搜索所走过的路径称为引用链
,当对象与GC Roots不可达(一个对象与GC Roots没有任何的引用链相连)时,代表此对象是不可用的,应该被回收。GC Roots常见对象:
1. 虚拟机栈的栈帧中(
局部变量表
)引用的对象2. 方法区中
类静态属性
引用的对象3. 方法区中
常量
引用的对象4. 本地方法栈中JNI
(Native方法)
引用的对象
引用计数算法弊端的代码:
public void testGC(){
ReferenceCount objA = new ReferenceCount();
ReferenceCount objB = new ReferenceCount();
/**
* 除了它们相互引用之外,没有任何引用,实际上这两个对象
* 已经不可能被外界访问了,但是正因为互相引用而导致引用
* 计数器不为0,所以引用计数算法无法通知GC收集器去收集它们。
*/
objA.instance = objB;
objB.instance = objA;
}
如上图,Object1 - Object4对象与GC Roots是有引用链的,但是Object5 - Object7虽然彼此之间有连通,但是它们没有与任何一个GC Roots相连(与GC Roots不可达),所以这三个对象在JVM看来是可回收的。
HotSpot虚拟机中的算法实现
枚举根节点
GC Roots主要存在全局性的引用(常量和类静态属性)和执行上下文(栈帧的本地变量表)中
1.1 可达性分析的执行对“引用一致性”非常地敏感,所以在枚举根节点时必须
停顿所有线程
“引用一致性”解释
当JVM进行可达性分析时,必须
保持当前的引用链是保持不变
的,否则分析结果有可能会出现偏差。例如:在分析某个对象时得出其余GC Roots不可达的结论,但是在分析完成之前此对象在某一个地方被重新引用,但是JVM是不会重复进行分析的,显然结果会不正确。1.2 缩短停顿时间
HotSpot通过一组
OopMap的数据结构
达到目的,在类加载完成时,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,并且存放在OopMap中,GC在扫描时遍历OopMap就可以得到所有引用关系了。
安全点(Safe Points)
如果在程序中含有大量的指令,对象引用关系不断变化,每一次变化都会生成一条新的OopMap,那么必然会导致OopMap也变得越来越庞大,遍历所使用的开销也会越来越大,此时使用安全点这种解决方案可以有效地解决这个问题。
解决
什么时候安全地进入GC
的问题,安全点能够让所有线程进行中断挂起。Safe Point的意义:
保证所有线程当前的所有引用状态不会发生变化
,所有线程要保证在安全点处中断。JVM进入GC阶段的两种线程中断方式(不是安全点的中断方式):
1. 抢先式中断:
在GC发生时,让所有的线程进行中断,
如果发现线程不是在安全点上,那么就恢复它,让它跑到安全点再进行中断
。现在几乎没有虚拟机采用抢先式中断来暂停线程进行响应GC事件,突然地中断和恢复线程会导致程序出现很奇怪的现象。2. 主动式中断:
当GC操作需要中断线程时,不直接对线程进行操作,而是
设置一个轮询标志
,让线程执行时主动轮询这一个标志,轮询标志为true时主动中断
;轮询标志的地方和安全点是重合
的,另外还有创建对象需要分配内存的地方也会有轮询标志。这样保证了线程执行到JVM认为该线程可以停止的地方
,而不会突然地中断线程了。Safe Point通常存在的位置:
1.方法调用处
2. 循环跳转处
3. 异常跳转处
4. 指令序列复用
Safe Points位置的选取特征:是否具有让程序
长时间运行
的特征。
安全区域(Safe Region)
如果程序不执行
,有可能处于Sleep或Blocked
状态,CPU没有分配给线程使用,此时线程肯定无法自己“跑”到安全点处再执行中断挂起,而JVM也不可能等待线程被唤醒,安全点这种方案无法满足GC的要求,所以此时需要采用安全区域方案。
安全区域是指在一段代码片段之中,引用关系不会发生变化,在此区域内的任何地方进行GC操作都是安全的,我们可以这样理解:
安全区域就是一小块含有无穷无尽的安全点的区域
。执行过程:
在线程进入Safe Origin时,线程将
主动标记自己进入了Safe Origin区域
,此时JVM发起GC时就不用在乎这些在Safe Origin的线程
了,当线程要离开Safe Origin时,它会检查系统是否已经完成了根节点枚举或者整个GC过程,如果已完成,则可以离开;若未完成,则需要等待允许离开Safe Origin的信号为止。
结尾
感谢你们阅读我编写的推文!今天主要是对是否回收对象的判断依据和对象可达性分析算法
进行了简单地介绍,如果我有任何写得不正确和不准确的地方,欢迎大家向我提出来,我们可以一起学习和交流!下一篇推文将会写垃圾回收算法
。
2019年9月25日
小菠萝 以上是关于JVM垃圾收集之可达性分析的主要内容,如果未能解决你的问题,请参考以下文章