在正式的GC以前,要进行可达性分析来标记出未来可能要宣告死亡的对象。若是每次GC的时候都要遍历全部的引用,这样的工做量是很是大的。由于在可达性分析的时候要保证期间不发生引用关系的变化,全部执行线程要停顿等待,称为“Stop The World”,程序中的线程须要中止来配合可达性分析。java
因此,每次直接遍历整个引用链确定是不现实的。 为了应对这种尴尬的问题,最先有保守式GC和后来的准确式GC。这里准确式GC就会提到一个OopMap,用来保存类型的映射表。安全
在进行GC的时候,会从一些已知的位置(GC Roots)开始扫描内存,扫描到一个数字就判断他是否是多是指向GC堆中的一个指针【这里会涉及上下边界检查(GC堆的上下界是已知的)、对齐检查(一般分配空间的时候会有对齐要求,假如说是4字节对齐,那么不能被4整除的数字就确定不是指针)等】,而后一直递归扫描下去,最后完成可达性分析。这种模糊的判断方法由于没法准确判断一个位置上是不是真的指向GC堆中的指针,因此被命名为保守式GC。oop
优势:这种可达性分析的方式由于不须要准确的判断出一个指针,因此效率快。线程
缺点:设计
与保守式GC相对的就是准确式GC。何为准确式GC?就是咱们准确的知道,某个位置上面是不是指针。对于java来讲,就是知道对于某个位置上的数据是什么类型的,这样就能够判断出全部的位置上的数据是否是指向GC堆的引用,包括栈和寄存器里的数据。指针
实现这种要求的方法有好几种,可是在java中实现的方式是:从外部记录下类型信息,存成映射表,在HotSpot中把这种映射表称之为OopMap,不一样的虚拟机名称可能不同。对象
实现这种功能,须要虚拟机的解释器和JIT编译器支持,由它们来生成OopMap。生成这样的映射表通常有两种方式:递归
总而言之,GC停顿的时候,虚拟机能够经过OopMap这样的一个映射表知道,在对象内的什么偏移量上是什么类型的数据,并且特定的位置记录着栈和寄存器中哪些位置是引用。内存
有了OopMap,HotSpot能够快速准确完成GC Roots枚举。可是另外一个问题来了,咱们要在什么地方建立OopMap?程序运行期间,引用的变化在不断发生,若每条指令都生成OopMap,那占用空间就太大了,因此有了安全点(Safe Point)。只在安全点进行GC停顿,只要保证引用变化的记录完成于GC停顿以前就能够。资源
安全点选定太少,GC等待时间就太长,选的太多,GC就过于频繁。选定原则是“具备让程序长时间执行的特征”,也就是在这个时刻现有的指令是能够复用的。通常选在方法调用、循环跳转、抛出异常的位置。
如何使线程在Safe Point停顿,方案有两种:抢先式中断(弃用)、主动式中断。
安全点能够保证大部分线程停顿,可是当GC请求中断时线程并无获取CPU执行权,线程没法响应JVM“跑到”安全点,可是JVM的GC不会等待线程得到CPU执行权再对它进行可达性分析或者回收。也就是说该线程如今在非安全点中止,而且GC对其进行了操做,这样的操做是不知足一致性的,当线程苏醒以后就会发生下面的状况,即线程在继续执行,GC也在操做该线程(这个过程对象的引用关系有可能会改变)。
安全区域(Safe Region)就能够很好地解决这样的问题:安全区域是指在一段代码片断中,引用关系不会发生变化,在该区域的任何地方发生GC中断请求都是安全的。当线程执行到安全区域时,首先标识本身已经进入了安全区域。在线程离开安全区域时,会检查系统是否正在执行GC,若是是,就等到GC完成后再离开安全区域。