垃圾收集器与内存分配策略(三)——HotSpot的算法实现
在HotSpot虚拟机上实现这些算法时,必须对算法的执行效率有着严格的考量,才能保证虚拟机高效地运行。
1、 枚举根节点
采用可达性分析从GC Roots节点中找引用链为例
存在的缺点:
1、在前面找出还存活对象时,采用可达性分析从GC Roots节点中找引用链时,可作为GC Roots的节点主要在全局性的引用(方法区的常量或类静态属性引用)与执行上下文(虚拟机栈栈帧中的本地变量表或本地方法栈中的Native方法的引用)中,很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,必然会消耗很多时间。
2、可达性分析对时间的敏感还体现在GC停顿上,因为这项工作必须在一个能确保一致性的快照中进行。“一致性值的是”GC进行时必须停顿所有Java执行线程(Stop The World)。
HotSpot的解决方式
1、当执行系统停下来时,不需要检查完所有的全局和执行上下文的引用位置,HotSpot采用一组称为OopMap的数据结构来记录那些地方存放着对象的引用。
2、JIT(即时编译)编译过程中也会在特定位置记录下栈和寄存器中那些位置是引用。
2、 安全点
如果为每一条指令都生成对应的Oopmap,会需要大量的额外空间,GC成本增高。其实HotSpot虚拟机并不是在为每条指令都生成了Oopmap,程序执行时也并非在任何地方都能停下来开始GC,只能到达特定位置才能开始记录,这些特定位置称为安全点(Safepoint)。
安全点的选择:是否具有让程序长时间执行的特征(比如:方法调用,循环跳转,异常跳转等)。
在GC发生时如何让所有线程跑到最近的安全点再停止有二种方案:
1、抢先式中断:不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复线程,让它“跑”到安全点上。 现在几乎没有虚拟机实现采用抢先式中断来暂停线程从而响应GC事件。
2、主动式中断:当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。
3、 安全区域
当程序不执行的时候即没有分配CPU时间,比如:线程处于Sleep状态或Blocked状态,对于这种情况就需要安全区域(Safe Region)来解决。
安全区域指在一段代码片段中,引用关系不会发生变化。在这个区域的任意地方开始GC都是安全的,或则可以将安全区域看做时扩展过得安全点。
安全区域工作原理:在线程中执行到安全区域的代码时,首先标识自己已经进入了安全区域,若在这段时间JVM要发起GC时,就不用管标识自己为安全区域状态的线程了。在线程执行完安全区域的代码要离开安全区域时,当前线程要检查当前系统是否已经完成了根节点枚举(或是整个GC过程),若系统已完成则可以离开安全区域;若系统未完成,则它就必须等待直到可以离开安全区域为止。