HotSpot 对算法的实现
2018-02-16 23:24
148 查看
前面垃圾收集器概述和垃圾收集算法两节从理论上介绍了对象存活判断算法和垃圾收集算法,本节则主要介绍HotSpot对上述算法的实现规则。
由于目前的主流java虚拟机使用的都是准确式GC,所以当执行系统停顿下来之后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,因为虚拟机是有办法直接得知哪些地方存放着对象引用的。那么是怎么实现的呢?在HotSpot的实现中,是用一组称为OopMap的数据结构来达到这个目的的。在类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样,GC在扫描时就可以直接得知这些信息了。
垃圾收集必须在一个能确保一致性的快照中进行,所谓“一致”是指在整个分析期间整个执行系统看起来就像被冻结在一个时间点上,不可能出现分析过程中对象引用关系不断变化的情况,如果不能保持这种一致性就无法保证结果的准确性。
这是导致GC进行时必须停顿所有java执行线程(Stop The World)的其中一个原因。即使是在号称几乎不会发生停顿的CMS收集器(一款垃圾收集器)中,枚举根节点时也是必须要停顿的。
由于目前的主流java虚拟机使用的都是准确式GC,所以当执行系统停顿下来之后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,因为虚拟机是有办法直接得知哪些地方存放着对象引用的。那么是怎么实现的呢?在HotSpot的实现中,是用一组称为OopMap的数据结构来达到这个目的的。在类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样,GC在扫描时就可以直接得知这些信息了。
准确式GC:就是让JVM知道内存中某位置数据的类型什么。比如当前内存位置中的数据究竟是一个整型变量还是一个引用类型。这样JVM可以很快确定所有引用类型的位置,从而更有针对性的进行GC roots枚举。
保守式GC:就是JVM并不知道内存中某位置数据的类型是什么。
因此,HotSpot没有为每条指令都生成OopMap,只是在特定位置记录这些信息。这些位置称为
所以安全点选定规则就变成了:是否具有让程序长时间执行的特征
因此具有较长运行时间的指令才能被选为安全点,如方法调用、循环跳转、异常跳转等。
这里有两种方案:
抢先式中断会把所有线程中断,如果某个线程不在安全点上,就恢复让它跑到安全点上。几乎没有虚拟机采用这种方式。
主动式中断思想是设立一个GC标志,各个线程会轮询这个标志并在需要时自己中断挂起。这样,标志和安全点是重合的。
安全区域是指在一段代码片段之中,引用关系不会发生变化。在这个区域中任意地方开始都是安全的。
在线程执行到安全区域中的代码时,就标记自己已经进入了安全区域,这样JVM在发起GC时就跳过这些线程。
在线程要离开安全区域时,它要检查系统是否已经完成了枚举(或GC过程),如果完成了线程就继续执行,否则就等待。
《垃圾回收的算法与实现》—— 保守式GC
枚举根节点
可达性算法中从GC Roots节点找到引用链这个操作可能会因为巨大的方法区而导致大量时间的消耗,而且可达性分析会因为GC停顿的原因而导致执行时间的敏感。由于目前的主流java虚拟机使用的都是准确式GC,所以当执行系统停顿下来之后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,因为虚拟机是有办法直接得知哪些地方存放着对象引用的。那么是怎么实现的呢?在HotSpot的实现中,是用一组称为OopMap的数据结构来达到这个目的的。在类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样,GC在扫描时就可以直接得知这些信息了。
垃圾收集必须在一个能确保一致性的快照中进行,所谓“一致”是指在整个分析期间整个执行系统看起来就像被冻结在一个时间点上,不可能出现分析过程中对象引用关系不断变化的情况,如果不能保持这种一致性就无法保证结果的准确性。
这是导致GC进行时必须停顿所有java执行线程(Stop The World)的其中一个原因。即使是在号称几乎不会发生停顿的CMS收集器(一款垃圾收集器)中,枚举根节点时也是必须要停顿的。
由于目前的主流java虚拟机使用的都是准确式GC,所以当执行系统停顿下来之后,并不需要一个不漏的检查完所有执行上下文和全局的引用位置,因为虚拟机是有办法直接得知哪些地方存放着对象引用的。那么是怎么实现的呢?在HotSpot的实现中,是用一组称为OopMap的数据结构来达到这个目的的。在类加载完成的时候HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中也会在特定的位置记录下栈和寄存器中哪些位置是引用。这样,GC在扫描时就可以直接得知这些信息了。
准确式GC:就是让JVM知道内存中某位置数据的类型什么。比如当前内存位置中的数据究竟是一个整型变量还是一个引用类型。这样JVM可以很快确定所有引用类型的位置,从而更有针对性的进行GC roots枚举。
保守式GC:就是JVM并不知道内存中某位置数据的类型是什么。
安全点
选择安全点
在OopMap协助下,HotSpot可以准确地完成GC Roots的枚举。但一个很现实的问题随之而来:可能导致引用关系发生变化,或者说OopMap内容变化的指令非常多,如果为每一条指令都生成对应的Oomap,那将需要大量的额外空间,这样GC的空间成本会非常高。因此,HotSpot没有为每条指令都生成OopMap,只是在特定位置记录这些信息。这些位置称为
安全点(Safepoint)。即程序执行时并非在所有位置都能停下来GC,只有到达安全点才可以暂停。安全点的选定不能太少以至于让GC等待时间太长,也不能过于频繁以至于过分增大运行时的负荷。
所以安全点选定规则就变成了:是否具有让程序长时间执行的特征
因此具有较长运行时间的指令才能被选为安全点,如方法调用、循环跳转、异常跳转等。
停顿到安全点
接下来要考虑的便是,如何在GC时保证所有的线程都“跑”到安全点上停顿下来。这里有两种方案:
抢先式中断(Preemptive Suspension)和
主动式中断(Voluntary Suspension)。
抢先式中断会把所有线程中断,如果某个线程不在安全点上,就恢复让它跑到安全点上。几乎没有虚拟机采用这种方式。
主动式中断思想是设立一个GC标志,各个线程会轮询这个标志并在需要时自己中断挂起。这样,标志和安全点是重合的。
安全区域
安全点机制可以保证某一程序在运行的时候,在不长的时间里就可以进入GC的安全点。但是如果程序没有分配CPU时间,例如处于Sleep状态或者Blocked状态,这时候线程无法响应JVM的中断请求。对于这种情况,只能用安全区域(Safe Region)来解决。安全区域是指在一段代码片段之中,引用关系不会发生变化。在这个区域中任意地方开始都是安全的。
在线程执行到安全区域中的代码时,就标记自己已经进入了安全区域,这样JVM在发起GC时就跳过这些线程。
在线程要离开安全区域时,它要检查系统是否已经完成了枚举(或GC过程),如果完成了线程就继续执行,否则就等待。
参考文献
JVM 之 OopMap 和 RememberedSet《垃圾回收的算法与实现》—— 保守式GC
相关文章推荐
- 垃圾收集器与内存分配策略-HotSpot算法实现
- 3.4 HotSpot算法实现
- HotSpot算法实现
- 6.《深入理解Java虚拟机》HotSpot 的算法实现
- JVM系列六(HotSpot的算法实现)
- java虚拟机HotSpot 的 GC 算法实现
- Java虚拟机垃圾回收(一) 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
- HotSpot的算法实现
- HotSpot的算法实现
- [深入理解Java虚拟机]第三章 HotSpot的垃圾收集算法实现
- 《JVM学习系列》四.垃圾收集算法及HotSpot的算法实现
- 深入理解java虚拟机(五):hotspot垃圾收集算法实现
- 深入理解java虚拟机(四)垃圾收集算法及HotSpot实现
- 5、HotSpot的算法实现
- 虚拟机3.4 HotSpot算法实现
- 《深入理解java虚拟机》学习笔记05--HotSpot中对象存活判读算法和垃圾收集算法的实现
- Java虚拟机垃圾回收(一) 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析
- HotSpot算法实现
- HotSpot 垃圾回收算法实现
- HotSpot的垃圾回收算法实现