美文网首页
java 安全点机制解析(编译执行)

java 安全点机制解析(编译执行)

作者: allanYan | 来源:发表于2023-01-08 15:51 被阅读0次

概述

我们都知道JVM中有很多操作,都是要求线程达到安全点,例如典型的垃圾回收,本文将深入细节,了解JVM的具体实现;
JAVA代码一开始通常是采用解释执行,当执行一定次数,发现是属于热点代码时,会通过JIT编译为机器代码,提供执行效率;
JIT编译时会插入一些安全点检查的指令,会查询当前Java线程的polling_page,正常情况下,查询polling_page没有什么问题;但是当VM线程开始执行时,会将polling_page改为bad page;在linux系统中,访问不可读页面会产生SIGSEGV信号:

JIT插入安全点指令

//c1_LIRAssembler.cpp:LIR_Assembler::emit_op1->LIR_Assembler::safepoint_poll
int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
  int offset = __ offset();
#ifdef _LP64
  const Register poll_addr = rscratch1;
  __ movptr(poll_addr, Address(r15_thread, JavaThread::polling_page_offset()));
#else
  const Register poll_addr = tmp->as_register();
  __ get_thread(poll_addr);
  __ movptr(poll_addr, Address(poll_addr, in_bytes(JavaThread::polling_page_offset())));
#endif
  add_debug_info_for_branch(info);
  __ relocate(relocInfo::poll_type);
  address pre_pc = __ pc();
  __ testl(rax, Address(poll_addr, 0));
  address post_pc = __ pc();
  return offset;
}

关键代码是这里的 __ movptr(poll_addr, Address(r15_thread, JavaThread::polling_page_offset()))

polling_page

polling_page到底是什么东西呢,可以从JVM的启动代码里面看到其初始化流程

//Threads::create_vm->SafepointMechanism::initialize()->SafepointMechanism::default_initialize

    const size_t page_size = os::vm_page_size();
    const size_t allocation_size = 2 * page_size;
    char* polling_page = os::reserve_memory(allocation_size);
    os::commit_memory_or_exit(polling_page, allocation_size, false, "Unable to commit Safepoint polling page");
    MemTracker::record_virtual_memory_type((address)polling_page, mtSafepoint);

    char* bad_page  = polling_page;
    char* good_page = polling_page + page_size;

    os::protect_memory(bad_page,  page_size, os::MEM_PROT_NONE);
    os::protect_memory(good_page, page_size, os::MEM_PROT_READ);

    log_info(os)("SafePoint Polling address, bad (protected) page:" INTPTR_FORMAT ", good (unprotected) page:" INTPTR_FORMAT, p2i(bad_page), p2i(good_page));

    // Poll address values
    _poll_page_armed_value    = reinterpret_cast<uintptr_t>(bad_page);
    _poll_page_disarmed_value = reinterpret_cast<uintptr_t>(good_page);
    _polling_page = (address)bad_page;

可以看到JVM启动时,申请了两个页,一个是可读的(good page),一个不可读(bad page);

VM线程

JVM中进入安全点的典型流程是通过VM线程,代码如下:

//vmThread.cpp:VMThread::inner_execute->SafepointSynchronize::begin->SafepointSynchronize::arm_safepoint

 for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
    // Make sure the threads start polling, it is time to yield.
    SafepointMechanism::arm_local_poll(cur);
  }
void SafepointMechanism::arm_local_poll(JavaThread* thread) {
  thread->poll_data()->set_polling_word(_poll_word_armed_value);
  thread->poll_data()->set_polling_page(_poll_page_armed_value);
}

进入安全点时,将将所有JAVA线程的_polling_page指向不可读页面(bad page);在linux系统中,访问一个MEM_PROT_NONE的页面会产生SIGSEGV;
而在JVM启动过程中,SIGSEGV信号已经被注册给了SafepointSynchronize::handle_polling_page_exception函数:

注册SIGSEGV信号

//os_linux_x86.cpp
bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
                                             ucontext_t* uc, JavaThread* thread) {
......
 if (sig == SIGSEGV && SafepointMechanism::is_poll_address((address)info->si_addr)) {
        stub = SharedRuntime::get_poll_stub(pc);
      }
......
}
address SharedRuntime::get_poll_stub(address pc) {
 bool at_poll_return = ((CompiledMethod*)cb)->is_at_poll_return(pc);
  bool has_wide_vectors = ((CompiledMethod*)cb)->has_wide_vectors();
  if (at_poll_return) {
    stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();
  } else if (has_wide_vectors) {
    stub = SharedRuntime::polling_page_vectors_safepoint_handler_blob()->entry_point();
  } else {
    stub = SharedRuntime::polling_page_safepoint_handler_blob()->entry_point();
  }
}

```c
void SharedRuntime::generate_stubs() {
......
_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);

......
}

SIGSEGV信号处理

ThreadSafepointState::handle_polling_page_exception最终通过SafepointSynchronize::block暂停业务线程:

if (global_poll()) {
      // Any load in ::block() must not pass the global poll load.
      // Otherwise we might load an old safepoint counter (for example).
      OrderAccess::loadload();
      SafepointSynchronize::block(thread);
    }

至此,安全点的触发机制都已经完全了解清楚;

相关文章

  • java 安全点机制解析(编译执行)

    概述 我们都知道JVM中有很多操作,都是要求线程达到安全点,例如典型的垃圾回收,本文将深入细节,了解JVM的具体实...

  • JVM内存模型与数据结构

    JAVA代码执行过程 JAVA源码编译由三个过程组成:源码编译机制、类加载机制、类执行机制。代码编译由JAVA源码...

  • java 安全点机制解析(解释执行)

    概述 在前面的文章中介绍了编译执行时,安全点的触发机制,本文将继续了解解释执行时,安全点是如何生效的; 字节码 T...

  • Java代码编译和执行的整个过程

    一、简述 Java代码编译和执行的整个过程包含了三个重要的机制:①Java源码编译机制;②类加载机制;③类执行机制...

  • Java奇怪的代码

    原因 -127 126 根据Java编译机制,.java文件在编译以后会生成.class文件给JVM加载执行...

  • JVM之编译机制、类加载机制(装载、验证、准备、解析、初始化、使

    大纲: JVM 一、编译机制 二、类加载机制(装载、验证、准备、解析、初始化、使用、卸载) 三、类执行机制 源码编...

  • Java 泛型

    Java泛型(generics)提供了编译时类型安全检测机制,该机制允许程序员啊在编译时检测到非法的类型,使更多的...

  • Java常用类库与技巧-异常

    一 Java异常体系 二 从概念角度解析Java的异常处理机制 Error : 程序无法处理的系统错误,编译器不做...

  • Java 泛型学习总结

    前言 Java 5 添加了泛型,提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质...

  • JVM类加载机制

    Java代码的执行机制 在JVM中执行的是编译过后的Class文件,classloader类加载器来负责加载cla...

网友评论

      本文标题:java 安全点机制解析(编译执行)

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