美文网首页
SafepointSynchronize

SafepointSynchronize

作者: 程序员札记 | 来源:发表于2022-12-21 08:35 被阅读0次

本文主要讲解GC的核心概念之安全点SafepointSynchronize的实现细节。

1、定义
SafepointSynchronize的定义位于hotspot\src\share\vm\runtime\safepoint.hpp中,用来实现安全点的进入和退出,其包含的属性如下:

  • static volatile SynchronizeState _state; // 表示安全点的状态
  • static volatile int _waiting_to_block; // 等待被阻塞(同步)的线程数
  • static int _current_jni_active_count; //记录安全点期间处于JNI关键区的线程的总数
  • static volatile int _safepoint_counter; //进入和退出安全点的总次数,进入和退出时都会加1
  • static long _end_of_last_safepoint; //上一次退出安全点的时间
  • static jlong _safepoint_begin_time; // 进入安全点的时间,单位是纳秒
  • static SafepointStats* _safepoint_stats; // SafepointStats数组,剩下几个参数都是跟SafepointStats配合使用,用来统计安全点相关的数据
  • static int _cur_stat_index; // current index to the above array
  • static julong _safepoint_reasons[]; // safepoint count for each VM op
  • static julong _coalesced_vmop_count; // coalesced vmop count
  • static jlong _max_sync_time; // maximum sync time in nanos
  • static jlong _max_vmop_time; // maximum vm operation time in nanos
  • static float _ts_of_current_safepoint; // 进入安全点的时间,单位是秒

其中SynchronizeState是一个枚举,用来描述安全点的状态,其定义如下:


image.png

其中0表示所有线程都不在安全点上,1表示所有线程在执行安全点同步中,2表示所有线程都停留在安全点上,只有VMThread在执行。

SafepointStats是一个数据结构,用来记录触发本次安全点的相关信息,其定义如下:

image.png

线程处于不同的状态下都有对应的机制让该线程进入到安全点,主要有以下几种:

  • 处于解释执行中,解释器通过字节码路由表(dispatch table)开始执行下一个字节码时会检查安全点的状态
  • 处于本地代码执行中,即正在执行JNI方法,当Java线程从JNI方法中退出后必须检查安全点的状态,VMThread并不会等待处于本地代码执行中的线程进入阻塞状态。
  • 处于编译执行中,编译代码在适当的位置上会读取Safepoint Polling内存页,如果该内存页是脏的,则表示需要进入安全点
  • 处于JVM执行中,即JVM执行上述三种状态的切换时,JVM会在切换前检查安全点的状态
  • 处于阻塞状态,即等待某个锁,JVM会一直阻塞该线程直到VMThead从安全点退出

线程的状态有一个对应的枚举JavaThreadState,其定义如下,其中奇数的都是中间状态,短时间内存在,偶数的是


image.png

2、os::serialize_thread_states / write_memory_serialize_page
该方法相当于执行OrderAccess::fence()方法,会强制各线程对线程状态的修改从高速缓存同步到内存中,从而方便SafepointSynchronize能够高效准确的获取线程状态,其实现如下:

void os::serialize_thread_states() {
  //SerializePageLock用于同步对_mem_serialize_page的写入操作,获取锁
  Thread::muxAcquire(&SerializePageLock, "serialize_thread_states");
  //将_mem_serialize_page对应的内存页先设置为只读,再设置为可读写,底层调用Linux的mprotect方法
  //Linux修改对应内存页的属性时会强制高速缓存中对该内存页的修改刷新到内存中
  os::protect_memory((char *)os::get_memory_serialize_page(),
                     os::vm_page_size(), MEM_PROT_READ);
  os::protect_memory((char *)os::get_memory_serialize_page(),
                     os::vm_page_size(), MEM_PROT_RW);
  //释放锁                   
  Thread::muxRelease(&SerializePageLock);
}
 
static address get_memory_serialize_page() {
    return (address)_mem_serialize_page;
  }
 
int os::vm_page_size() {
  assert(os::Linux::page_size() != -1, "must call os::init");
  return os::Linux::page_size();
}

_mem_serialize_page是通过set_memory_serialize_page方法初始化的,该方法的实现如下:

void os::set_memory_serialize_page(address page) {
  int count = log2_intptr(sizeof(class JavaThread)) - log2_int(64);
  _mem_serialize_page = (volatile int32_t *)page;
  //SerializePageShiftCount是一个常量,64位下取值为4
  assert(SerializePageShiftCount == count,
         "thread size changed, fix SerializePageShiftCount constant");
  //vm_page_size返回内存页的大小,64位下默认是4k,4096字节
  //sizeof(int32_t)是32位int的大小,是4字节
  set_serialize_page_mask((uintptr_t)(vm_page_size() - sizeof(int32_t)));
}
 
static void     set_serialize_page_mask(uintptr_t mask) {
    _serialize_page_mask = mask;
  }

_mem_serialize_page怎么跟线程状态有关联了?答案在write_memory_serialize_page,该方法的实现如下:

  static inline void write_memory_serialize_page(JavaThread *thread) {
    uintptr_t page_offset = ((uintptr_t)thread >>
                            get_serialize_page_shift_count()) &
                            get_serialize_page_mask();
    //将指定位置的4字节数据修改为1,即最多支持1024个线程                        
    *(volatile int32_t *)((uintptr_t)_mem_serialize_page+page_offset) = 1;
  }
 
    static int     get_serialize_page_shift_count() {
    //SerializePageShiftCount是一个常量,值为4
    return SerializePageShiftCount;
  }

该方法主要是线程状态流转时会调用,其调用链:


image.png

当执行os::serialize_thread_states期间,如果有其他线程并行的执行write_memory_serialize_page方法且_mem_serialize_page是只读的,则执行write_memory_serialize_page方法的Linux内核线程会终止对应的用户线程的执行,然后向该线程发送SIGSEGV(表示操作指定的内存段错误)信号量,JVM负责处理信号的线程接受到该信号后,判断触发SIGSEGV的内存地址位于_mem_serialize_page中,则会调用os::block_on_serialize_page_trap方法阻塞该用户线程,直到os::serialize_thread_states执行完成,然后重新执行修改,block_on_serialize_page_trap的实现如下:

void os::block_on_serialize_page_trap() {
  if (TraceSafepoint) {
    tty->print_cr("Block until the serialize page permission restored");
  }
  //尝试去获取锁,会阻塞当前线程直到获取成功,获取成功表示serialize_thread_states方法执行完成
  Thread::muxAcquire(&SerializePageLock, "set_memory_serialize_page");
  //再次释放该锁
  Thread::muxRelease(&SerializePageLock);
}
 
static bool    is_memory_serialize_page(JavaThread *thread, address addr) {
    if (UseMembar) return false;
    if (thread == NULL) return false;
    //判断addr是否在_mem_serialize_page中
    address page = (address) _mem_serialize_page;
    return addr >= page && addr < (page + os::vm_page_size());
  }

该方法的调用链如下:

image.png

具体的调用如下:


image.png

3、 TemplateInterpreter::notice_safepoints / ignore_safepoints
notice_safepoints用于通知解释器进入安全点了,ignore_safepoints用于通知解释器退出安全点了,这两方法的实现如下:

void TemplateInterpreter::notice_safepoints() {
  if (!_notice_safepoints) {
    //如果_notice_safepoints为false,即未进入安全点,将_notice_safepoints置为true
    _notice_safepoints = true;
    //将_safept_table复制到_active_table,即执行字节码时会执行_safept_table中的对应字节码的执行逻辑
    copy_table((address*)&_safept_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address));
  }
}
 
void TemplateInterpreter::ignore_safepoints() {
  if (_notice_safepoints) {
    //如果_notice_safepoints为true,表示已经进入安全点
    if (!JvmtiExport::should_post_single_step()) {
      //将_notice_safepoints置为false
      _notice_safepoints = false;
      //将_normal_table复制到_active_table
      copy_table((address*)&_normal_table, (address*)&_active_table, sizeof(_active_table) / sizeof(address));
    }
  }
}
 
static inline void copy_table(address* from, address* to, int size) {
  //每次复制8字节的数组,DispatchTable就是一个address二维数组,因此在不加锁的情况下也能保证解释器依然正常执行
  //在解释器并行执行时,部分已复制的字节码走检查安全点的逻辑,未复制的字节码继续走原来的逻辑,不检查安全点
  while (size-- > 0) *to++ = *from++;
}
 
typedef u_char*       address;

TemplateInterpreter的相关知识可以参考《Hotspot 方法调用之TemplateInterpreter 源码解析》,这个_safept_table里面有啥东西了?可以搜索该属性的调用链,如下:


image.png

第二个方法set_safepoints_for_all_bytes就是其初始化的方法,其实现如下:

void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() {
  for (int i = 0; i < DispatchTable::length; i++) {
    Bytecodes::Code code = (Bytecodes::Code)i;
    if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, 
Interpreter::_safept_entry);
  }
}

其中_safept_entry就是字节码的解释执行的入口,该属性在哪初始化的了?搜索其调用链,如下:


image.png

generate_all中相关方法的实现如下:

{ CodeletMark cm(_masm, "safepoint entry points");
    Interpreter::_safept_entry =
      EntryPoint(
        //btos等表示栈顶缓存的值类型
        generate_safept_entry_for(btos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(ztos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(ctos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(stos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(ftos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(dtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),
        generate_safept_entry_for(vtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint))
      );
  }
 
address TemplateInterpreterGenerator::generate_safept_entry_for(
        TosState state,
        address runtime_entry) {
  //获取CodeCache的top地址,作为此执行Stub的地址
  address entry = __ pc();
  __ push(state);
  //调用runtime_entry,即 InterpreterRuntime::at_safepoint方法
  __ call_VM(noreg, runtime_entry);
  //vtos表示栈顶是空的,进入此表示安全点已经结束,切换到_normal_table即正常的不检查安全点的字节码执行入口上
  __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos));
  return entry;
}
 
//执行安全点检查的核心就在IRT_ENTRY的定义中
IRT_ENTRY(void, InterpreterRuntime::at_safepoint(JavaThread* thread))
 
  if (JvmtiExport::should_post_single_step()) {
    //JVMTI通知
    JvmtiExport::at_single_stepping_point(thread, method(thread), bcp(thread));
  }
IRT_END
 
#define IRT_ENTRY(result_type, header)                               \
  result_type header {                                               \
    //ThreadInVMfromJava负责将线程的状态从in java调整为in vm,并且在at_safepoint方法执行完成调用ThreadInVMfromJava
    //的析构函数时执行安全点检查,从而实现在执行完当前字节码并切换到下一个字节码执行前让Java线程停在安全点上
    ThreadInVMfromJava __tiv(thread);                                \
    VM_ENTRY_BASE(result_type, header, thread)                       \
    debug_only(VMEntryWrapper __vew;)
 
#define VM_ENTRY_BASE(result_type, header, thread)                   \
  TRACE_CALL(result_type, header)                                    \
  HandleMarkCleaner __hm(thread);                                    \
  Thread* THREAD = thread;                                           \
  os::verify_stack_alignment();                                      \
#define IRT_END }

在析构函数中检查安全点的ThreadInVMfromJava的实现如下:

class ThreadInVMfromJava : public ThreadStateTransition {
 public:
  ThreadInVMfromJava(JavaThread* thread) : ThreadStateTransition(thread) {
    //将线程状态从_thread_in_Java切换成_thread_in_vm
    trans_from_java(_thread_in_vm);
  }
  ~ThreadInVMfromJava()  {
    //将线程状态从_thread_in_vm切换成_thread_in_Java
    trans(_thread_in_vm, _thread_in_Java);
    //检查是否有异常
    if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition();
  }
};
 
ThreadStateTransition(JavaThread *thread) {
    _thread = thread;
    assert(thread != NULL && thread->is_Java_thread(), "must be Java thread");
}
 
void trans_from_java(JavaThreadState to)              { transition_from_java(_thread, to); }
 
static inline void transition_from_java(JavaThread *thread, JavaThreadState to) {
    assert(thread->thread_state() == _thread_in_Java, "coming from wrong thread state");
    //直接切换,未检查安全点
    thread->set_thread_state(to);
  }
 
//transition方法会检查是否进入安全点,如果是则阻塞当前线程
 void trans(JavaThreadState from, JavaThreadState to)  { transition(_thread, from, to); }

4、os::make_polling_page_unreadable / make_polling_page_readable
这两个方法都是用来给Java编译代码通知安全点的状态,make_polling_page_unreadable是进入安全点时调用的,让_polling_page变得不可访问,make_polling_page_readable是退出安全点时调用的,_polling_page变得只读。其实现如下:

void os::make_polling_page_unreadable(void) {
  if( !guard_memory((char*)_polling_page, Linux::page_size()) )
    fatal("Could not disable polling page");
};
 
void os::make_polling_page_readable(void) {
  if( !linux_mprotect((char *)_polling_page, Linux::page_size(), PROT_READ)) {
    fatal("Could not enable polling page");
  }
};
 
bool os::guard_memory(char* addr, size_t size) {
  //PROT_NONE表示让指定的内存页不可访问,如果其他线程访问了,则内核线程会终止执行,并给用户线程发送SIGSEGV信号
  return linux_mprotect(addr, size, PROT_NONE);
}

_polling_page通过set_polling_page方法初始化,同_mem_serialize_page,都是在os::init的时候调用的;通过get_polling_page方法访问_polling_page;通过is_poll_address方法判断某个地址是否在_polling_page中,这几个方法的实现比较简单,如下:


image.png

其中 get_polling_page方法的调用链如下:


image.png

其中checkPollingPage就是接受到SIGSEGV信号后调用的处理逻辑,其他方法都是编译器调用的,checkPollingPage的实现如下:

inline static bool checkPollingPage(address pc, address fault, address* stub) {
  if (fault == os::get_polling_page()) {
    //如果导致错误的地址就是_polling_page,则将stub赋值为get_poll_stub
    *stub = SharedRuntime::get_poll_stub(pc);
    return true;
  }
  return false;
}
 
address SharedRuntime::get_poll_stub(address pc) {
  address stub;
  //找到pc对应的CodeBlob,即保存编译代码的nmethod
  CodeBlob *cb = CodeCache::find_blob(pc);
 
  //校验cb是nmethod
  guarantee(cb != NULL && cb->is_nmethod(), "safepoint polling: pc must refer to an nmethod");
 
  // Look up the relocation information
  assert( ((nmethod*)cb)->is_at_poll_or_poll_return(pc),
    "safepoint polling: type must be poll" );
 
  assert( ((NativeInstruction*)pc)->is_safepoint_poll(),
    "Only polling locations are used for safepoint");
 
  bool at_poll_return = ((nmethod*)cb)->is_at_poll_return(pc);
  bool has_wide_vectors = ((nmethod*)cb)->has_wide_vectors();
  //返回SharedRuntime的对应执行Stub
  if (at_poll_return) {
    assert(SharedRuntime::polling_page_return_handler_blob() != NULL,
           "polling page return stub not created yet");
    stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();
  } else if (has_wide_vectors) {
    assert(SharedRuntime::polling_page_vectors_safepoint_handler_blob() != NULL,
           "polling page vectors safepoint stub not created yet");
    stub = SharedRuntime::polling_page_vectors_safepoint_handler_blob()->entry_point();
  } else {
    assert(SharedRuntime::polling_page_safepoint_handler_blob() != NULL,
           "polling page safepoint stub not created yet");
    stub = SharedRuntime::polling_page_safepoint_handler_blob()->entry_point();
  }
 
  return stub;
}
 
 static SafepointBlob* polling_page_return_handler_blob()     { return _polling_page_return_handler_blob; }
  static SafepointBlob* polling_page_safepoint_handler_blob()  { return _polling_page_safepoint_handler_blob; }
  static SafepointBlob* polling_page_vectors_safepoint_handler_blob()  { return _polling_page_vectors_safepoint_handler_blob; }

 checkPollingPage方法在JVM_handle_linux_signal中的调用逻辑如下:

if (thread->thread_state() == _thread_in_Java) {
      do {
      
        if ((sig == SIGSEGV) && checkPollingPage(pc, (address)info->si_addr, &stub)) {
          break;
        }
 
        if ((sig == SIGBUS) && checkByteBuffer(pc, &stub)) {
          break;
        }
        //其他的信号处理逻辑
 
        if ((sig == SIGSEGV) &&
            checkNullPointer(pc, (intptr_t)info->si_addr, thread, &stub)) {
          break;
        }
      } while (0);
 
      // jni_fast_Get<Primitive>Field can trap at certain pc's if a GC kicks in
      // and the heap gets shrunk before the field access.
      if ((sig == SIGSEGV) || (sig == SIGBUS)) {
        checkFastJNIAccess(pc, &stub);
      }
    }
 
    if (stub != NULL) {
      //保存出现异常时代码的执行位置
      thread->set_saved_exception_pc(pc);
      thread->set_saved_exception_npc(npc);
      //开始执行stub
      set_cont_address(uc, stub);
      return true;
    }
  }

SharedRuntime中三个与安全点相关的Stub都在SharedRuntime::generate_stubs()方法中完成初始化的,相关代码如下:

#ifdef COMPILER2
  if (is_wide_vector(MaxVectorSize)) {
    _polling_page_vectors_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_VECTOR_LOOP);
  }
#endif // COMPILER2
  //generate_handler_blob用于生成一个通用的处理器Blob,会先保存当前寄存器和栈帧中的数据,然后执行具体的handler方法,handler方法执行完成后再将之前保存的数据恢复到寄存器和栈帧中,即恢复之前的执行现场环境
  _polling_page_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_LOOP);
  _polling_page_return_handler_blob    = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN);
 
 
void SafepointSynchronize::handle_polling_page_exception(JavaThread *thread) {
  //校验当前线程是Java线程
  assert(thread->is_Java_thread(), "polling reference encountered by VM thread");
  //校验线程的状态
  assert(thread->thread_state() == _thread_in_Java, "should come from Java code");
  //校验安全点的状态
  assert(SafepointSynchronize::is_synchronizing(), "polling encountered outside safepoint synchronization");
 
  if (ShowSafepointMsgs) {
    tty->print("handle_polling_page_exception: ");
  }
 
  if (PrintSafepointStatistics) {
    inc_page_trap_count();
  }
  //获取该线程对应的ThreadSafepointState
  ThreadSafepointState* state = thread->safepoint_state();
  //执行polling_page的异常处理
  state->handle_polling_page_exception();
}
 
inline static bool is_synchronizing()  { return _state == _synchronizing;  }
 
void ThreadSafepointState::handle_polling_page_exception() {
  //检查ThreadSafepointState的状态
  assert(type() == ThreadSafepointState::_running,
         "polling page exception on thread not running state");
 
  if (ShowSafepointMsgs && Verbose) {
    tty->print_cr("Polling page exception at " INTPTR_FORMAT, thread()->saved_exception_pc());
  }
  //根据出现异常的地址找到对应的nmethod
  address real_return_addr = thread()->saved_exception_pc();
  CodeBlob *cb = CodeCache::find_blob(real_return_addr);
  assert(cb != NULL && cb->is_nmethod(), "return address should be in nmethod");
  nmethod* nm = (nmethod*)cb;
 
  //找到出现异常的方法的调用方
  frame stub_fr = thread()->last_frame();
  CodeBlob* stub_cb = stub_fr.cb();
  assert(stub_cb->is_safepoint_stub(), "must be a safepoint stub");
  RegisterMap map(thread(), true);
  frame caller_fr = stub_fr.sender(&map);
 
  assert( nm->is_at_poll_or_poll_return(real_return_addr), "should not be at call" );
 
  if( nm->is_at_poll_return(real_return_addr) ) {
    //判断该方法的返回类型是否是一个oop
    bool return_oop = nm->method()->is_returning_oop();
    Handle return_value;
    if (return_oop) {
      //如果是则将其放在Hanlder里保护起来
      oop result = caller_fr.saved_oop_result(&map);
      assert(result == NULL || result->is_oop(), "must be oop");
      return_value = Handle(thread(), result);
      assert(Universe::heap()->is_in_or_null(result), "must be heap pointer");
    }
 
    //阻塞当前线程,直到VMThead从安全点退出
    SafepointSynchronize::block(thread());
 
    if (return_oop) {
      //设置调用结果oop
      caller_fr.set_saved_oop_result(&map, return_value());
    }
  }else {
    set_at_poll_safepoint(true);
    //校验当前方法的返回地址就是调用方的pc
    assert(real_return_addr == caller_fr.pc(), "must match");
 
    //阻塞当前线程,直到VMThead从安全点退出
    SafepointSynchronize::block(thread());
    set_at_poll_safepoint(false);
 
    //deoptimize
    if (thread()->has_async_condition()) {
      ThreadInVMfromJavaNoAsyncException __tiv(thread());
      Deoptimization::deoptimize_frame(thread(), caller_fr.id());
    }
 
    if (thread()->has_pending_exception() ) {
      RegisterMap map(thread(), true);
      frame caller_fr = stub_fr.sender(&map);
      if (caller_fr.is_deoptimized_frame()) {
        fatal("Exception installed and deoptimization is pending");
      }
    }
  }
}

综上,当Java线程执行编译代码时,是通过SIGSEGV信号的处理机制来实现线程阻塞的,并且在VMThread从安全点退出后重新恢复执行。从get_polling_page的调用链判断,编译代码应该是在一个本地方法执行完成后调用get_polling_page的,如下:


image.png

5、JavaThread::check_safepoint_and_suspend_for_native_trans
该方法就是从Java代码中调用本地方法时,本地方法在执行完成检查安全点的实现,其实现如下:

void JavaThread::check_special_condition_for_native_trans(JavaThread *thread) {
  check_safepoint_and_suspend_for_native_trans(thread);
 
  if (thread->has_async_exception()) {
    //处理线程执行过程中的异常
    thread->check_and_handle_async_exceptions(false);
  }
}
 
void JavaThread::check_safepoint_and_suspend_for_native_trans(JavaThread *thread) {
  //校验线程的状态
  assert(thread->thread_state() == _thread_in_native_trans, "wrong state");
 
  JavaThread *curJT = JavaThread::current();
  //判断Java线程是否被要求挂起
  bool do_self_suspend = thread->is_external_suspend();
 
  assert(!curJT->has_last_Java_frame() || curJT->frame_anchor()->walkable(), "Unwalkable stack in native->vm transition");
 
  if (do_self_suspend && (!AllowJNIEnvProxy || curJT == thread)) {
    //如果要求对当前线程挂起
    //获取原来的状态
    JavaThreadState state = thread->thread_state();
 
    //_suspend_equivalent被设置为true
    thread->set_suspend_equivalent();
 
    //修改线程状态为_thread_blocked
    thread->set_thread_state(_thread_blocked);
    //将当前线程挂起
    thread->java_suspend_self();
    //恢复原来的状态
    thread->set_thread_state(state);
    //刷新高速缓存中的线程状态到内存中
    if (os::is_MP()) {
      if (UseMembar) {
        OrderAccess::fence();
      } else {
        InterfaceSupport::serialize_memory(thread);
      }
    }
  }
 
  if (SafepointSynchronize::do_call_back()) {
    //如果在安全点上则阻塞当前线程
    SafepointSynchronize::block(curJT);
  }
 
  if (thread->is_deopt_suspend()) {
    //如果需要逆向优化
    thread->clear_deopt_suspend();
    RegisterMap map(thread, false);
    frame f = thread->last_frame();
    while ( f.id() != thread->must_deopt_id() && ! f.is_first_frame()) {
      f = f.sender(&map);
    }
    if (f.id() == thread->must_deopt_id()) {
      thread->clear_must_deopt_id();
      f.deoptimize(thread);
    } else {
      fatal("missed deoptimization!");
    }
  }
}
 
int JavaThread::java_suspend_self() {
  int ret = 0;
 
  //如果线程正在退出中
  if (is_exiting()) {
     clear_external_suspend();
     return ret;
  }
 
  assert(_anchor.walkable() ||
    (is_Java_thread() && !((JavaThread*)this)->has_last_Java_frame()),
    "must have walkable stack");
  
  //SR_lock就是用于线程自旋等待的锁
  MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);
 
  assert(!this->is_ext_suspended(),
    "a thread trying to self-suspend should not already be suspended");
 
  if (this->is_suspend_equivalent()) {
    //如果_suspend_equivalent为true,则将其置为false
    this->clear_suspend_equivalent();
  }
 
  while (is_external_suspend()) {
    ret++;
    this->set_ext_suspended();
    //不断循环等待java_resume()方法获取SR_lock锁,将ext_suspended置为false,并唤醒当前线程
    while (is_ext_suspended()) {
      this->SR_lock()->wait(Mutex::_no_safepoint_check_flag);
    }
  }
 
  return ret;
}
 
static inline void serialize_memory(JavaThread *thread) {
  os::write_memory_serialize_page(thread);
}

该方法的调用链如下:

image.png

其中InterpreterGenerator::generate_native_entry方法用于生成从Java代码调用本地方法的执行入口 。SharedRuntime::generate_native_wrapper方法用于生成从Java的编译代码中调用本地方法的执行入口,同样也是在本地方法执行完成后校验是否进入安全点了,如果是则阻塞当前线程。

6、ThreadStateTransition
ThreadStateTransition负责线程状态的流转,是各种场景下线程状态流转的最终实现,重点关注以下三个方法的实现:

static inline void transition(JavaThread *thread, JavaThreadState from, JavaThreadState to) {
    //校验状态不能是_thread_in_Java和_thread_in_native,从调用链看通常应该是_thread_in_vm
    assert(from != _thread_in_Java, "use transition_from_java");
    assert(from != _thread_in_native, "use transition_from_native");
    //校验from和to都是偶数状态,即不能是中间的转换状态,如_thread_in_native_trans
    assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");
    //校验当前状态就是from
    assert(thread->thread_state() == from, "coming from wrong thread state");
    //将状态置为中间的转换状态,如_thread_in_vm到_thread_in_vm_trans
    thread->set_thread_state((JavaThreadState)(from + 1));
 
    //如果系统支持多线程
    if (os::is_MP()) {
      //如果使用内存栅栏指令,x86下默认为false
      if (UseMembar) {
        // Force a fence between the write above and read below
        OrderAccess::fence();
      } else {
        //修改memory_serialize_page,这样当进入安全点时可以通过os::serialize_thread_states实现相同的高速缓存强制刷新的效果
        os::write_memory_serialize_page(thread);
      }
    }
    //如果进入安全点了,则阻塞当前线程
    if (SafepointSynchronize::do_call_back()) {
      SafepointSynchronize::block(thread);
    }
    //没有安全点或者从安全点退出,则修改线程状态为to
    thread->set_thread_state(to);
 
    CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
  }
 
static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) {
    //校验当前状态就是from
    assert(thread->thread_state() == from, "coming from wrong thread state");
    //校验from和to都是非中间状态
    assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");
    //修改为中间状态
    thread->set_thread_state((JavaThreadState)(from + 1));
 
    //如果系统支持多线程
    if (os::is_MP()) {
      //如果使用内存栅栏指令,x86下默认为false
      if (UseMembar) {
        OrderAccess::fence();
      } else {
        InterfaceSupport::serialize_memory(thread);
      }
    }
 
    if (SafepointSynchronize::do_call_back()) {
      //如果进入安全点了,则阻塞当前线程
      SafepointSynchronize::block(thread);
    }
    //没有安全点或者从安全点退出,则修改线程状态为to
    thread->set_thread_state(to);
 
    CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
  }
 
//从_thread_in_native修改为其他状态
static inline void transition_from_native(JavaThread *thread, JavaThreadState to) {
    //校验to是非中间状态
    assert((to & 1) == 0, "odd numbers are transitions states");
    //校验当前线程的状态
    assert(thread->thread_state() == _thread_in_native, "coming from wrong thread state");
    //设置为中间状态
    thread->set_thread_state(_thread_in_native_trans);
 
    if (os::is_MP()) {
      if (UseMembar) {
        OrderAccess::fence();
      } else {
        InterfaceSupport::serialize_memory(thread);
      }
    }
 
    if (SafepointSynchronize::do_call_back() || thread->is_suspend_after_native()) {
      //如果进入安全点或者需要挂起当前线程
      JavaThread::check_safepoint_and_suspend_for_native_trans(thread);
      CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
    }
 
    thread->set_thread_state(to);
  }

从其实现可知,只要发生了线程状态变更就会检查是否进入了安全点,如果进入了则阻塞当前线程,transition的调用链如下:


image.png

其中JavaCallWrapper就是本地代码通过JNI接口调用Java代码时使用的,在执行前会通过构造函数将线程状态由_thread_in_vm切换成_thread_in_Java,等调用结束通过析构函数再切换成_thread_in_vm。ThreadInVMfromJava是解释器使用的,通过构造和析构函数完成Java线程状态的变化。transition_and_fence的调用链如下:

image.png

其中ThreadBlockInVM是需要将当前线程阻塞或者挂起时使用,其调用链如下:

image.png

ThreadInVMfromNative主要是check版的JNI接口使用的,其调用链如下:


image.png

ThreadToNativeFromVM是JVM执行底层系统方法如加载zip文件时使用的,其调用链如下:


image.png

transition_from_native的调用链如下:


image.png

即执行上述调用链中的操作时都需要改变当前线程的状态,状态改变时就会校验是否进入安全点,如果进入了安全点则会阻塞当前线程。

相关文章

网友评论

      本文标题:SafepointSynchronize

      本文链接:https://www.haomeiwen.com/subject/hpnyqdtx.html