1.OopMapjava
以前咱们提到,在正式的GC以前老是须要进行可达性分析来查找内存中全部存活的对象,以便GC可以正确的回收已经死亡的对象。那么对于一个十分复杂的系统,每次GC的时候都要遍历全部的引用确定是不现实的。由于在可达性分析的时候,须要进行Stop The World,程序中的线程须要中止来配合可达性分析。就好像是你女友在打扫卫生的时候(什么,你尚未女友?这还能难道程序员了?new 一个啊!),确定不会让你走来走去的。因此,你确定在心里里也但愿你女友打扫卫生快一点,由于你的膀胱已经快要爆炸了。对于程序来讲有也同样,也但愿GC的时候快一点,以便让程序高效地完成工做。程序员
因此,每次直接遍历整个引用链确定是不现实的。 为了应对这种尴尬的问题,最先有保守式GC和后来的准确式GC。这里准确式GC就会提到一个OopMap,用来保存类型的映射表。算法
在进行GC的时候,会从一些已知的位置(GC Roots)开始扫描内存,扫描到一个数字就判断他是否是多是指向GC堆中的一个指针(这里会涉及上下边界检查(GC堆的上下界是已知的)、对齐检查(一般分配空间的时候会有对齐要求,假如说是4字节对齐,那么不能被4整除的数字就确定不是指针),之类的。)。而后一直递归的扫描下去,最后完成可达性分析。这种模糊的判断方法由于没法准确判断一个位置上是不是真的指向GC堆中的指针,因此被命名为保守式GC。这种可达性分析的方式由于不须要准确的判断出一个指针,因此效率快,可是也正由于这种特色,他存在下面两个明显的缺点:安全
2.准确式GC性能
与保守式GC相对的就是准确式GC,何为准确式GC?就是咱们准确的知道,某个位置上面是不是指针,对于java来讲,就是知道对于某个位置上的数据是什么类型的,这样就能够判断出全部的位置上的数据是否是指向GC堆的引用,包括栈和寄存器里的数据。线程
网上看了下说是实现这种要求的方法有好几种,可是在java中实现的方式是:从我外部记录下类型信息,存成映射表,在HotSpot中把这种映射表称之为OopMap,不一样的虚拟机名称可能不同。设计
实现这种功能,须要虚拟机的解释器和JIT编译器支持,由他们来生成OopMap。生成这样的映射表通常有两种方式:指针
总而言之,GC开始的时候,就经过OopMap这样的一个映射表知道,在对象内的什么偏移量上是什么类型的数据,并且特定的位置记录下栈和寄存器中哪些位置是引用。对象
2.SafePoint(安全点)递归
上面讲到了为了快点进行可达性的分析,使用了一个引用类型的映射表,能够快速的知道对象内或者栈和寄存器中哪些位置是引用了。
可是随着而来的又有一个问题,就是在方法执行的过程当中, 可能会致使引用关系发生变化,那么保存的OopMap就要随着变化。若是每次引用关系发生了变化都要去修改OopMap的话,这又是一件成本很高的事情。因此这里就引入了安全点的概念。
什么是安全点?OopMap的做用是为了在GC的时候,快速进行可达性分析,因此OopMap并不须要一发生改变就去更新这个映射表。只要这个更新在GC发生以前就能够了。因此OopMap只须要在预先选定的一些位置上记录变化的OopMap就好了。这些特定的点就是SafePoint(安全点)。由此也能够知道,程序并非在全部的位置上均可以进行GC的,只有在达到这样的安全点才能暂停下来进行GC。
既然安全点决定了GC的时机,那么安全点的选择就至为重要了。安全点太少,会让GC等待的时间太长,太多会浪费性能。因此安全点的选择是以程序“是否具备让程序长时间执行的特征”为标准的(这句话是从书上看来的,不知道做者本身能不能看明白这话啥意思,反正我是看不懂),因此咱们这里了解一下结果就好了。通常会在以下几个位置选择安全点:
还有一个须要考虑的问题就是,如何让程序在要进行GC的时候都跑到最近的安全点上停顿下来。这里有两种方案:
抢断式中断就是在GC的时候,让全部的线程都中断,若是这些线程中发现中断地方不在安全点上的,就恢复线程,让他们从新跑起来,直到跑到安全点上。(如今几乎没有虚拟机采用这种方式,缘由不详)
主动式中断在GC的时候,不会主动去中断线程,仅仅是设置一个标志,当程序运行到安全点时就去轮训该位置,发现该位置被设置为真时就本身中断挂起。因此轮训标志的地方是和安全点重合的,另外建立对象须要分配内存的地方也须要轮询该位置。
3.安全区域
安全点的使用彷佛解决了OopMap计算的效率的问题,可是这里还有一个问题。安全点须要程序本身跑过去,那么对于那些已经停在路边休息或者看风景的程序(好比那些处在Sleep或者Blocked状态的线程),他们可能并不会在很短的时间内跑到安全点去。因此这里为了解决这个问题,又引入了安全区域的概念。
安全区域很好理解,就是在程序的一段代码片断中并不会致使引用关系发生变化,也就不用去更新OopMap表了,那么在这段代码区域内任何地方进行GC都是没有问题的。这段区域就称之为安全区域。线程执行的过程当中,若是进入到安全区域内,就会标志本身已经进行到安全区域了。那么虚拟机要进行GC的时候,发现该线程已经运行到安全区域,就不会管该线程的死活了。因此,该线程在脱离安全区域的时候,要本身检查系统是否已经完成了GC或者根节点枚举(这个跟GC的算法有关系),若是完成了就继续执行,若是未完成,它就必须等待收到能够安全离开安全区域的Safe Region的信号为止。