JVM - HotSpot 的算法实现(HotSpot 虚拟机如何优化算法实现)

前面文章中从理论上介绍了对象存活判定(这里为可达性分析算法)和垃圾收集算法,而在HotSpot虚拟机上实现这些算法时,必须对算法的执行效率有严格的考量,才能保证虚拟机高效运行。

HotSpot虚拟机在发生GC时所产生的问题以及解决这些问题的方案

问题提出一(时间):

1. 在可达性分析算法来判定对象是否存活时,需要执行的动作是检查该对象是否被引用链引用,可以预见实际的应用中对象是很多的,这里逐个检查会占用很多的时间。

2. 可达性分析算法分析结果准确的前提是GC停顿。也是占用时间。

针对时间问题的解决方案:

上面问题产生的原因是,虚拟机因为不知道哪些地方存放着的是对象的引用,他必须笨笨的去全局检查。假如虚拟机在类加载完成的时候就已经知道哪些地方存放的是对象的引用,那么就不用再去瞎找一番。目前的虚拟机使用的都是准确式GC有办法直接得知哪些地方存放着对象的引用。在HotSpot的实现中使用一组成为OopMap的数据结构来在特定位置记录哪些些地方存放的是引用。这样在 GC 发生时,HotSpot 就可以直接扫描 OopMap 来获取对象引用的存储位置,从而进行 GC Roots 枚举。也相应的减少了GC停顿的时间。OopMap的作用就是帮助HotSpot虚拟机快速且准确的完成GC Roots枚举。

问题提出二(空间):

在OopMap的协助下,HotSpot可以快速准确的完成GC Roots,但是一个很现实的问题随之而来:可能导致引用关系的变化(为什么会导致这个变化,是我太较真了吗),或者说OopMap内容变化的指令很多,如果为每一条指令都生成对应的OopMap,那么会产生大量的额外空间,这样GC的空间成本很高。

针对空间问题的解决方案:

前面讲到HotSpot只是在 “特定的位置”记录了OopMap信息,这些位置成为安全点。安全点即程序执行时并非在所有的地方都能停顿下来开始GC(不能随地大小便),只有在到达安全点时才能暂停。安全点的选定既不能太少以致于让GC等待时间太长,也不能太过于频繁以致于过分增大运行时的负荷。因此,安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”来选定的长时间运行即指令序列复用,例如方法调用、循环跳转、异常跳转等

小问题1:在GC发生时,如果程序还没跑到最近的安全点该怎么办?

有两种方案选择:

抢先式中断:抢先式中断不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复该线程让他跑到安全点。现在虚拟机都不采用这种方式。

主动式中断: 当GC需要中断时,不直接对线程操作,仅仅简单设置一个标志,各线程主动去轮询这个标志,发现中断标志时就自己中断挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。

问题提出三(线程sleep或者blocked):

安全点机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的安全点。但是对于不执行(即没有分配CPU时间)的程序,如线程处于sleep或Blocked状态,线程就无法响应JVM的中断请求,走到安全点去挂起。JVM也不太可能等待线程重新被分配CPU时间。这时就需要安全区域来解决了。

安全区域即在一段代码片段中,引用关系不会发生变化。在这个区域任意地方开始GC都是安全的。可以看作是被扩展了的安全点。

线程到达安全区域时,先标识自己进入安全区域。当这段时间内JVM要发起GC时,就不用管标识安全区域状态的线程了。当线程要离开安全区域时,先检查系统是否完成了根结点枚举或整个GC过程,完成了就继续执行,没有就等待直到收到可以安全离开安全区域的信号为止。
 

 

猜你喜欢

转载自blog.csdn.net/qq_29676623/article/details/84450586