①枚举根节点
可达性分析中,查找引用链这个操作,可作为GC Roots的节点主要在全局性的引用中(例如常量和静态属性)和执行上下文(例如栈帧中的本地变量表)中,现在很多应用本地方法区就有数百找,要一个个查引用的话,太费时间,另外,可达性分析对时间的敏感性还体现在GC停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行,这里的一致性指的是整个分析期间整个执行系统看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不断变化的情况,这点事导致GC进行时必须停顿所有Java执行线程的一个重要原因。
由于现在的虚拟机使用的都是准确式GC,所以当执行系统停顿下来,并不需要一个不漏地检查完所有的执行上下文和全局的引用位置,虚拟机应当是有办法直接得知哪些地方存放着对象引用,在HotSpot中,使用一组称为OopMap的数据结构来达到这个目的,在类加载完成的时候,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。
也就是说通过OopMap的帮助,能够很快枚举出GC Roots的位置。
②安全点(Safepoint)
程序执行时不会随意停顿下来GC,只会在达到安全点的时候停下。也就是JIT编译过程,那些记录栈和寄存器中哪些位置是引用的特定的位置。
安全点的选定以“是否具有让程序长时间执行的特征”为标准进行选定的,“长时间执行”的最明显特征就是指令序列复用,比如循环跳转,方法调用。只有具有这些功能的指令才会产生Safepoint
另外,线程怎么在GC的时候都到最近的安全点上再停顿下来?
有两种方法“抢先试中断”(线程被动中断,如果线程中断的地方不在安全点上,就恢复线程,让他到中断点上)和“主动式中断”(设置一个标志,各个线程执行时主动去轮询这个标志,发现为真时就自动挂起,轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方)
③安全区域(Safe Region)
安全点很好的解决了程序执行时GC的问题,但是程序不执行(也就是CPU没有分配时间,比如线程处于sleep时)的时候就需要安全区域来解决。
安全区域指的是在一段代码片段之间,引用关系不会发生变化,在这个区域任何地方开始GC都是安全的
当线程执行到安全区域的代码时,首先标识自己已经进入安全区域,那样,这段时间JVM要发起GC,就不用管表示自己为安全区域的线程,在线程要离开安全区域时,要检查系统是否已经完成了根节点枚举(或者是整个GC过程),如果完成了,那线程就继续执行,否则就必须等待直到收到可以来开的信号。因为如果GC还没完成,那么这些在safe region中的线程也是被stop the world所影响的线程的一部分,如果让他们可以正常执行了,可能会影响标记的结果