【jvm学习笔记八】G1-线程中的安全点

在垃圾回收中,经常会STW,为了首先STW,jvm设计了安全点(safepoint)。那么什么是安全点?线程什么时候进入和离开安全点呢?

安全点概念

安全点:安全点可以理解为一些特殊位置,当代码执行到这些特殊位置时,则当前虚拟机的状态是安全可控的。这里的安全可控是指,虚拟机可以通过VM线程找到活跃对象,能够检查或更新Mutator线程状态等。
当Mutator达到安全点后,可以主动放弃CPU,让VM线程执行。让Mutator在安全点停止的原因有2个:1. 让VMThread能够原子的运行,不受Mutator的干扰,2. 在安全点停止更容易实现。当需要STW时,会产生一个VM_Operation并放入VMThread队列中,VMThread线程会循环处理这个队列里的请求。但在真正处理VM_Operation前需进入安全点,之后需要恢复安全点。代码如下:

void VMThread::loop() {
  assert(_cur_vm_operation == NULL, "no current one should be executing");

  while(true) {
      ....
      // 进入安全点
      SafepointSynchronize::begin();
      //执行VM_Operation
      evaluate_operation(_cur_vm_operation);
       // 退出安全点
      SafepointSynchronize::end();
    }
  }
}

进入安全点的方式

  1. G1并发线程进入安全点
    像G1新引入的ConcurrentRefineThread、ConcurrentMarkThread和G1StringDedupThread线程,当这些线程在内部工作时会调用join,离开时候调用leave,主动放弃时调用yield。其中,用join判断VMThread是否发出了安全点的请求,若发出则等待。等并发线程leave后,VMThread就可以工作了。并发线程在leave后,在做下一次工作时,又会再调用join,若VMThread发出请求,
    就会进入等待。

  2. 解释线程进入安全点
    如果Mutator是解释执行,即通过模板解释器执行字节码,那么该如何放弃CPU,JVM提供了一个正常的指令派发表和异常指令派发表,当需要进入安全点时,JVM会用异常指令派发表代替正常的指令派发表,那么线程执行完当前指令后就会进入异常指令派发表,异常指令派发表所有的栈顶状态缓存都会执行进入安全点的方法,从而进入安全点。

  3. 编译线程进入安全点
    编译线程进入安全点是借助于Linux信号完成的。JVM在初始化时会生成一个全局的轮询页面。JIT在编译时会插入额外的汇编代码去轮询这个页面的状态,若不可读则产生一个SIGSEGV,JVM则会处理这个信号并最终将线程阻塞。

  4. 正在执行本地代码的线程进入安全点
    如果线程正在运行本地代码,是无法访问Java对象的,此时VMThread可与本地代码并发运行。当此线程切换到Java代码执行的时候,那么就需要让这个线程暂停。JVM是通过在设置标志位来实现的,当切换到Java代码时,若发现标志位已设置则让自己暂停。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容