第三章: Hotspot算法实现

枚举根节点
可达性分析算法中,需要根据GC Roots节点下找出所有存活的引用(例如常量或者静态属性)与执行上下文(例如栈帧在宏的本地变量表)。GC Roots中的方法区可能会有数百兆,如果逐个检索这里的引用,那么必然会消耗很多时间。
可达性分析算法堆执行时间的敏感还体现在GC停顿上,因为这项分析公司必须在一个能确保一致性的快照中进行---这里的一致性是指整个分析期间整个执行系统看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不断变化的情况,该点不满足的话,分析结果无法得到保证。这个GC 停顿被称为Stop the World。即使被称为不会发生停顿的CMS收集器中,枚举根节点时也必须要停顿。
当前主流Java虚拟机使用的都是标准式GC,所以当系统停顿下来时,不需要一个不漏的检测完所有执行上下文和全局的引用变量,在hotspot虚拟机中使用一组OopMap的数据结构来存储哪些地方存放着对象引用。Hotspot把对象内什么偏移量上的什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样GC在扫描时就可以间接得知这些信息了。
 
安全点
在OopMap的帮助下,Hotspot能很快完成GC Roots的检索,但是需要在一个停顿中才可以保证GC Roots检索过程中引用关系不变。
Hotspot没有为每条指令都提供OopMap,而是在特定的位置记录了这些信息,这些位置被称为安全点SafePoint。SafePoint安全点的选择即不能太少以至于让GC等待时间太长,也不能太多以至于过分增大运行时的负荷。所以安全点的选择以程序“是否具有让程序长时间执行的特征”为标配进行选择,“长时间执行”的最明显特征就是指令序列复用、例如方法调用、循环跳出、异常跳出等,所以以上这些功能的指令才会产生SafePoint。
另一个问题,SafePoint如何在GC发生时让所有线程(这里不包括JNI线程)都跑到最近的SafePoint上再停顿下来,这里提供了两种方式:
  • 抢占式中断(现在几乎没有虚拟机使用这种方式)
在发生GC时,首先停顿所有线程,如果发现有线程不在安全点上,就恢复线程,让其跑到安全点上
  • 主动式中断
当GC需要中断线程时,不直接对线程操作,而是设置一个标志,各个线程执行时主动轮训这个标志,发现中断标志为真时自己中断挂起。轮询标志的位置与安全点时重合的,另外再加上创建对象需要分配内存的地方。
 
安全区域
使用SafePoint似乎能够完美的解决如果进入GC的问题,但实际上却并不一定。
SafePoint机制保证了程序执行时,在不太长的时间内就会遇到可以进入GC的SafePoint,但是,在程序“不执行”(即程序没有被分配CPU执行时间),典型的就是线程处于Sleep状态或者Blocked状态,这时候线程无法响应JVM的中断请求,所以无法跑到SafePoint点上进行挂起,这时候就需要被称为安全区域的Safe Region来解决。
安全区域是指在一段代码中引用关系不会发生变化。在这个区域的任何地方,开始GC都是安全的。
在线程执行到safe region中的代码时,首先标识自己已经进入了safe region,这样当在这段时间内JVM要进行GC时,就不要管标识自己为safe region状态的线程了。在线程要离开safe region代码时,它需要检查系统是否完成了根节点枚举(或者整个GC过程),如果完成了,那线程就继续执行,否则它就必须等待知道收到可以安全离开safe region的信号为止。
收藏文章数量从多到少与“把书读薄”是一个道理
原文地址:https://www.cnblogs.com/use-D/p/10640175.html