引言
判断对象是否被回收的算法有引用计数算法和可达性分析算法。其中可达性分析算法是根据GC Roots根节点作为起始点向下搜索引用链,找不到引用链则判定对象可回收。
可作为GC Roots根节点的对象主要是在全局性的引用(如常量、类静态属性)和执行上下文中(如栈帧中的本地变量表),现在的很多应用仅方法区就有数百兆,逐个检查里边的引用显然很耗费时间。
枚举GC Roots根节点
在HotSpot实现中,利用了空间换取时间,是使用一组OopMap的数据结构来完成的。类加载完成后,会把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,在特定的位置(即安全点)使用OopMap记录下栈和寄存器哪些位置是引用,这样GC发生的时候就不用全部扫描了。
安全点
如果每条指令都生成OopMap那将需要大量的额外空间。此时在特定的位置即安全点使程序不是在所有的地方都停顿(Stop The World)下来开始GC,只有在到达安全点时才停顿开始GC。安全点的选定不能使GC等待时间过长也不能使GC频繁触发,如具有方法调用、循环跳转、异常跳转的指令才会有安全点。
另一个问题是GC发生时如何让所有的线程都跑到安全点附近停顿下来?两种方案:抢先式中断和主动式中断。
抢先式中断,GC发生时首先让所有的线程都停顿下来,然后让还没到安全点的线程恢复,跑到安全点。几乎没有虚拟机采用这种方式响应GC。
主动式中断,GC发生需要中断所有的线程时,不直接对线程操作而是设置一个中断标志,该标志和安全点位置重合,各线程执行时都会去轮询该中断标志,如果线程发现该标志为真时就自己中断挂起。
安全区域
程序运行时安全点可以完美的解决GC触发的问题,但是当程序不执行时即没有分配到CPU时间时,此时线程不能响应JVM的中断请求,JVM显然不可能等待线程分配到CPU时间跑到安全点时再开始GC。这是需要安全区域来解决问题。
安全区域是指一段代码段中引用关系不会发生变化,在该区域何时何地开始GC都是安全的。线程执行安全区域的代码块时会标识自己已经进入了安全区域,此时如果JVM发起GC线程不会再标志中断状态标识,线程离开安全区域时会检查GC枚举GC Roots根节点(或者是整个GC过程)是否已经完成,如果完成了就继续执行,没完成的话就等待GC完成回收任务收到可以离开的信号再离开安全区域。