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虚拟机中的算法实现

  1. 枚举根节点

GC Roots主要存在全局性的引用(常量和类静态属性)和执行上下文(栈帧的本地变量表)中

1.1 可达性分析的执行对“引用一致性”非常地敏感,所以在枚举根节点时必须停顿所有线程

  • “引用一致性”解释

当JVM进行可达性分析时,必须保持当前的引用链是保持不变的,否则分析结果有可能会出现偏差。例如:在分析某个对象时得出其余GC Roots不可达的结论,但是在分析完成之前此对象在某一个地方被重新引用,但是JVM是不会重复进行分析的,显然结果会不正确。

1.2 缩短停顿时间

  • HotSpot通过一组OopMap的数据结构达到目的,在类加载完成时,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,并且存放在OopMap中,GC在扫描时遍历OopMap就可以得到所有引用关系了。

  1. 安全点(Safe Points)

    如果在程序中含有大量的指令,对象引用关系不断变化,每一次变化都会生成一条新的OopMap,那么必然会导致OopMap也变得越来越庞大,遍历所使用的开销也会越来越大,此时使用安全点这种解决方案可以有效地解决这个问题。

解决什么时候安全地进入GC的问题,安全点能够让所有线程进行中断挂起。

Safe Point的意义:

保证所有线程当前的所有引用状态不会发生变化,所有线程要保证在安全点处中断。

JVM进入GC阶段的两种线程中断方式(不是安全点的中断方式):

1. 抢先式中断:

  • 在GC发生时,让所有的线程进行中断,如果发现线程不是在安全点上,那么就恢复它,让它跑到安全点再进行中断。现在几乎没有虚拟机采用抢先式中断来暂停线程进行响应GC事件,突然地中断和恢复线程会导致程序出现很奇怪的现象。

2. 主动式中断:

  • 当GC操作需要中断线程时,不直接对线程进行操作,而是设置一个轮询标志,让线程执行时主动轮询这一个标志,轮询标志为true时主动中断轮询标志的地方和安全点是重合的,另外还有创建对象需要分配内存的地方也会有轮询标志。这样保证了线程执行到JVM认为该线程可以停止的地方,而不会突然地中断线程了。

Safe Point通常存在的位置

    1.方法调用处

    2. 循环跳转处

    3. 异常跳转处

    4. 指令序列复用

Safe Points位置的选取特征:是否具有让程序长时间运行的特征。

  1. 安全区域(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垃圾收集之可达性分析的主要内容,如果未能解决你的问题,请参考以下文章

JVM垃圾回收篇(垃圾回收算法)

深入浅出JVM之垃圾收集算法

JVM高级特性-垃圾收集算法

深入浅出JVM之垃圾收集算法

JVM探究之 —— 垃圾回收

JVM高级特性-垃圾收集之判断对象存活算法