【005】【JVM——Hotspot算法实现存活对象判定和垃圾收集】



Hotspot算法实现存活对象判断和垃圾收集

枚举根节点

作为GC Roots 的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中,它们的数量庞大,逐个检查里面的引用,会消耗很多时间。其次,准确的可达性分析需要暂停用线程的执行。

目前的主流Java 虚拟机使用的都是准确式GC (准确式内存管理Exact Memory Management:虚拟机知道内存中某个位置的数据类型具体是什么),所以当执行系统停顿下米后, 并不需要一个不漏地检验所有执行上下文和全局的引用位置,虚拟机有办法直接得知哪些地方存放着对象引用。在HotSpot 的实现中, 使用一组称为OopMap 的数据结构来实现这个目的,在类加载完成的时候, HotSpot 就把对象内,什么偏移量上是什么类型的数据计算出来,在JIT 编译过程中,也会在特定的位置记录下战和号寄存器中哪些位置是引用。GC 在扫描时就可以直接知道这些信息了。

 

安全点

为每条指令都生成OopMap,消耗大量空间,GC成本很高。HotSpot也没有这样做,只在特定位记录这些信息,这些位置被称为“安全点”,即程序执行时并非所有地方都可以停下来开始GC,只有在到达安全点时才能暂停。安全点的选择是以程序“是否具有让程序长时间执行的特征”了标准进行选定的。最明显的就是指令复用,如方法调用,循环跳转。

如何让所有线程(不包括执行JNI 调用的线程)都“ 跑”到最近的安全点上再停顿下来。有两种方案抢先式中断( Preemptive Suspension )和主动式中断( Voluntary Suspension )

  • 抢先式中断:不需要线程的执行代码主动去配合,在GC 发生时,首先把所有线程全部中断,如果发现有钱程中断的地方不在安全点上,就恢复线程,让它“跑”到安全点上. 现在几乎没有虚拟机实现。

  • 主动式中断:当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现标志为真时就自己中断挂起。

轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。

安全区域

命案点解决了如何进入GC 的问题,问题还是存在,如果线程处于睡眠状态或者阻塞状态,此时线程无法响应JVM的中断请求,不能“走”到安全的地方去中断挂起。 JVM 也不可能等待线程重新被分配CPU 时间。这样就需要要安全区域( Safe Region )来解决。

安全区域:指在一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC 都是安全的,可以把安全区域看做是被扩展了的安全点。在线程执行到安全区域中的代码时,首先标识自己已经进入了安全区域,这段时间内JVM 发起GC,JVM不用管标识为安全区域状态的钱程了。线程离开安全区域时,它要检查系统是否已经完成了根节点枚举(或者是整个GC 过程),如果完成了,线程就继续执行,否则就必须等待直到收到可以安全离开安全区域的信号。

 

【参见】【深入理解Java虚拟机(第二版)】【周志明】

点赞