HotSpot在GC时如何实现对象存活判断和垃圾收集

1. 枚举根节点

之前介绍了在GC过程中要先判断对象是否存活,用到了可达性分析算法,通过GC Roots节点找引用链。可作为GC Root的节点主要是全局性引用(例如常量和静态变量),与执行上下文(栈帧中的本地变量表)中。那么如何在这么多的全局变量和栈中的局部变量表中找到栈上的根节点呢?在栈中只有一部分数据是Reference(引用)类型,那些非Reference的类型的数据对于找到根节点没有什么用处,如果我们对栈全部扫描一遍这是相当浪费时间和资源的事情。所以要通过枚举来找到GC Roots根节点。

可达性分析对执行时间是敏感的,体现在GC停顿上,该分析过程中不能出现对象引用关系还在不断变化的情况,这导致了GC进行时必须停顿所有Java执行线程,因此枚举根节点时也是必须要停顿的。

目前主流的Java虚拟机用的都是准确式GC,当系统停顿后不需要全局扫面引用位置,借助OopMap这种数据结构,虚拟机可以直接得知哪些地方存放着对象引用,因此可以快速完成枚举根节点。

2. 安全点

OopMap内容变化的指令很多,不能为每一条指令都生成对应的OopMap,HotSpot虚拟机是在特定位置生成了OopMap,这些位置叫做“安全点”。程序执行时只有到了安全点才会暂停下来开始GC。一般具有“长时间执行”这个特点的指令(例如方法调用、循环跳转、异常跳转)才会产生安全点。

如何在GC时让所有线程都跑到安全点上再停顿下来?两种方案:

(1) 抢先式中断。(现在几乎没有虚拟机采用该方法)

不需要线程主动配合,在GC发生的时候就让所有线程都中断,如果发现哪个线程中断的地方不在安全点上,那么就恢复线程,然后让它跑到安全点上。

(2) 主动式中断

GC在需要中断线程的时候不直接对线程操作,设置一个标志,让各个线程主动轮询这个标志,如果中断标志位真时就让自己中断。

3. 安全区域

使用“安全点”似乎完美解决了如何进入GC的问题,安全点机制保证了程序执行时在不太长的时间内就会遇到安全点,从而进入GC。但是当程序不执行的时候(即当线程处于Sleep或者Blocked状态),线程无法响应JVM的中断请求而走到安全点去中断挂起,JVM也不会等待线程重新被唤醒,这时就需要安全区域来解决。

安全区域是指在一段代码片段中,引用关系不会发生变化,在该区域的任何地方发生GC都是安全的。当代码执行到安全区域时,首先标识自己已经进入了安全区域,那样如果在这段时间里JVM发起GC,就不用管标识自己在安全区域的那些线程了,在线程离开安全区域时,会检查系统是否正在执行GC,如果是那么就等到GC完成后再离开安全区域。

扫描二维码关注公众号,回复: 245329 查看本文章

猜你喜欢

转载自my.oschina.net/u/3342874/blog/1801545