HopSpot进入GC的算法实现
1.枚举根节点
前文提过虚拟机通常采用可达性分析的算法来判断对象是否还存活
在从gc root向下查找引用链时,可作为GC ROOT的节点主要在全局性引用(常量、静态变量)和执行上下文(栈帧中的本地变量表),通常方法区就有数百兆,逐个检查消耗会很大
因为可达性分析必须在一个能够保证一致性的快照中进行,所以在进行枚举根节点时必须STW(停止所有线程)来进行可靠的可达性分析,因此枚举根节点的效率对虚拟机的效率影响非常大
- OopMap:
在HotSpot中,使用的是一种称为OopMap的数据结构来存储对象内什么偏移量存储的是什么类型的数据的映射关系,在JIT编译过程中,也会在特定位置记录下栈和寄存器中的那些位置和引用的,这样GC在扫描时就可以直接获得这些信息,极大的提高了进行可达性分析的效率.
2.安全点
- 问题:
OopMap很好,但也有问题存在:可能导致引用关系变化.或者说如果OopMap内容变化的指令非常多,如果每一条指令都生成对应的OopMap,会需要大量的额外空间,这样GC的空间成本会变的非常高.
对此,hotspot选择只有在被设定为安全点的指令时才进行OopMap维护 - 选定:
安全点的选定通常是以是否能让程序长时间执行的特征选定的.例如方法调用、执行跳转、异常跳转等处. - 暂停线程方案:
抢先式中断和主动式中断
抢先式中断:在GC发生时首先将所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复线程,让他运行到安全点上,几乎没有虚拟机采用这种算法
主动式中断:当GC需要中断线程时不直接对线程进行操作,而是设置一个标识.当各个线程执行时轮询这个标识,当标识为真时把自己中断挂起.轮询的地方和安全点重合,再加上创建对象需要分配内存的地方
3.安全区域
安全点检查仿佛完全解决了如何进入GC的问题,但只有安全点还是不够的,安全点只解决了那些在运行的程序,保证了他们可以运行到安全点并挂起,但如果有些线程此时并未执行,例如处于sleep或blocked状态的线程,就无法响应JVM的中断请求,这是就用到了安全区域
设置安全点的意义就在于处于安全点的线程中的引用关系不会发生变化,所以在此进行GC是安全的.若线程处于sleep或blocked状态,本身引用关系就不会发生变化,所以可以把处于sleep或blocked状态也视为安全的,在这个状态下的线程被称为处于安全区域.
当线程执行到安全区域时首先把自己标识为已经进入安全区域,这样,在JVM需要发起GC时,可以不必考虑已经标识自己为安全区域状态的线程.
但是GC过程也需要时间,所以在线程即将离开安全区域时,首先要检查系统是否已经完成了GC过程,如果完成就继续执行,否则需要等待JVM发出可以安全离开安全区域的信号