jvm原理(45)安全点与安全区域详解

CMS

concurrent mark sweep

枚举根节点

  • 当执行系统停顿下来后,并不需要一个不漏的检查完所有执行上下文呵呵全局的引用位置,虚拟机应当是由办法直接得知那些地方存放着对象引用。在hotspot
    的实现中,是使用一组称为OopMap的数据结构来达到这个目的的。

安全点

-在OopMap的协助下,HotSpot可以快速且准确地完成GC Roots枚举,但一个很现实的问题随之而来,可能导致引用关系变化,或者说OopMap内容变化的指令
非常多,如果为每一条指令都生成对应的OopMap,那将会需要大量的额外空间,这样GC的空间成本将会变得更高。

  • 实际上,HotSpot并没 有为每条指令都生成OopMap,而是只在“特定的位置”记录了这些信息,这些位置称为“安全点(safepoint)”,即程序执行时并非
    所有地方都能停顿下来开始GC,只有在达到安全点时才能暂停。
  • Safepoint的选定既不能太少以至于GC等待时间太长,也不能过于频繁以至于过分增大运行时的负载,所以,安全点的选定基本上是以“是否具有程序长时间执行的特征”
    为标准选定的—因为每条指令执行的时间非常短暂,程序不可能因为指令流长度太长这个原因而过长时间运行,“长时间执行”的最明显特征就是指令序列
    复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生Safepoint。
  • 对于safepoint,另一个需要考虑的问题是如何在GC发生时让所有线程(这里不包括执行jni调用的线程)都“跑”到最近的安全点上再停顿下来:抢占式中断
    (Preemtive Suspension)和主动式中断(voluntary suspension)
  • 抢占式中断:他不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果有线程中断的地方不在安全点上,就恢复线程,让它“跑”
    在安全点上。
  • 主动式中断:当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就
    自己中断挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。
  • 现在几乎没有虚拟机采用抢占式中断来暂停线程而响应GC事件。

安全区域

  • 在使用safepoint似乎已经完美地解决了如何进入GC的问题,但实际上情况却并不一定。safepoint机制保证了程序执行时,在太长的时间内就会遇到
    可进入GC的safepoint。但是如果程序在“不执行”的时候呢?所谓程序不执行就是没有分配CPU时间,典型的例子就是处于sleep状态或者blocked状态,
    这时候程序无法响应jvm的中断请求,jvm也显然不可能等待线程重新分配cpu时间,对于这种情况,就需要安全区域(saferegin)来解决。

  • 在线程执行到saferegion中的代码时,首先标示自己已经进入safe region,那样,当在这段时间里jvm要发起GC时,就不用标示自己为safe region状态的
    状态的线程了,在线程要离开safe region时,他要检查系统是否已经完成了根节点枚举(或者整个gc过程),如果完成了,那线程就继续执行,
    否则他就必须等待直到收到可以安全离开safe region的信号为止。