HotSpot 回收算法

枚举根节点

从前面的文章中可以看到, 内存回收需要使用一种方法来判断对象时候已经死亡,而实现这种判断的最主流的方法就是可达性分析。关于可达性分析的具体方法前面已经有介绍,此处不提。

由于可达性分析需要从根节点 GC roots 出发,沿着引用链行进,来查看哪些对象没有在引用路径上,从而回收掉它们。而这个引用关系显然不能随时变化,所以需要在 GC 的时候,将所有的 Java线程停顿下来。Sun公司给这个事件起了一个牛逼闪闪的名字, Stop the world。

那么,当整个 Java 线程都停下来时, 虚拟机怎么知道哪些地方存在着对象引用呢?

这需要一种数据结构,OopMap。在类加载时, HotSpot 把对象内什么偏移量上是什么类型的数据计算出来,这样当 gc 在扫描时就可以直接得知这些信息了。

在OopMap的协助下,GC 可以快速准确地完成 GC Roots的枚举。

安全点

程序在执行时,并非在在所有的地方都能停顿下来开始 GC, 只有在安全点时才能暂停

安全点的选择既不能太少,以至于让 GC 等待的时间太长, 也不能太多,增大运行时的负担。

安全点的选定标准:是否具有让程序长时间执行的特征,在JVM中每条指令的执行时间都非常短暂,所以自然不能让执行时间太长的指令长时间运行,这些指令比如循环,方法调用,异常跳转。

那么,如何在GC发生时让线程跑到安全点呢?

方法有二:

1.抢先式抢断(Preemptive Suspension),在GC发生时首先把所有的线程都中断,如果发现线程中断的地方不在安全点上,就恢复线程。这种方法采用较少。

2.主动式抢断(Voluntary Suspension),当GC需要中断线程时,不直接对线程操作,仅仅简单地设置一个标志,相当于在广场上大吼一声“我要回收垃圾了”,然后各个线程执行时主动轮询这个标志,发现中断标志为真,则自己主动中断挂起。什么时候轮询呢?就在安全点上轮询。这种方法较为主流。

安全区域

上面所说的都是在线程执行的前提下,不然怎么轮询呢?那么问题来了,线程不处于执行状态怎么办?比如,阻塞,睡眠。

这个时候,就需要靠安全区域机制来实现了。

安全区域是指在一段代码时间之内,如果引用关系不发生变化,在这个区域中的任意地方开始 GC 都是安全的。