我们知道,在分析一个对象是否是存活的时候有两种方法,一个是引用计数法,引用计数法虽然实现简单并且效率较高,但是很难解决循环引用。所以目前主流的虚拟机都是使用的是:可达性分析法。在可达性分析法中对象能被回收的条件是没有引用来引用它,要做到这点就需要得到所有的GC Roots节点,来从GC Root来遍历。可作为GC Root的主要是全局性引用(例如常量和静态变量),与执行上下文(栈帧中的本地变量表)中。那么如何在这么多的全局变量和栈中的局部变量表中找到栈上的根节点呢?
在栈中只有一部分数据是Reference(引用)类型,那些非Reference的类型的数据对于找到根节点没有什么用处,如果我们对栈全部扫描一遍这是相当浪费时间和资源的事情。
那怎么做可以减少回收时间呢?我们很自然的想到可以用空间来换取时间,我们可以在某个位置把栈上代表引用的位置记录下来,这样在gc发生的时候就不用全部扫描了,在HotSpot中使用的是一种叫做OopMap的数据结构来记录的。对于OopMap可以简单的理解是存放调试信息的对象。
在OopMap的协助下,我们可以快速的完成GC Roots枚举,但我们也不能随时随地都生成OopMap,那样一方面会需要更多的空间来存放这些对象,另一方面效率也会简单低下。所以只会在特定的位置来记录一下,主要是正在:
- 循环的末尾
- 方法临返回前/调用方法的call指令后
- 可能抛异常的位置
这些位置称为安全点。
关于安全点:
我们在做GC的时候需要让jvm停在某个时间点上,如果不是这样我们在分析对象间的引用关系的时候,引用关系还在不断的变化。这样我们的准确性就无法得到保证。 安全点就是所有的线程在要GC的时候停顿的位置。那么如何让所有的线程都到安全点上在停顿下来呢?这里有两种方案可以选择:
- 抢先式中断
- 主动式中断
在抢先式中断中不需要线程主动配合,在GC发生的时候就让所有线程都中断,如果发现哪个线程中断的地方不在安全点上,那么就恢复线程,然后让它跑到安全点上。
而主动式中断是让GC在需要中断线程的时候不直接对线程操作,设置一个标志,让各个线程主动轮询这个标志,如果中断标志位真时就让自己中断。
目前几乎没有虚拟机采用抢先式中断了。
网友评论