HotSpotVM创建过程(JNI_CreateJavaVM)详解

来自:《Java Performance》第3章 JVM Overview


The HotSpot VM’s implementation of the JNI_CreateJavaVM method performs the following sequence of operations when it is called during the launch of the HotSpot VM.


1. Ensure no two threads call this method at the same time and only one HotSpot VM instance is created in the process. Because the HotSpot VM creates static data structures that cannot be reinitialized, only one HotSpot VM can be created in a process space once a certain point in initialization is reached. To the engineers who develop the HotSpot VM this stage of launching a HotSpot VM is referred to as the “point of no return.”


2. Check to make sure the Java Native Interface version is supported, and the output stream is initialized for garbage collection logging.


3. The OS modules are initialized such as the random number generator, the current process id, high-resolution timer, memory page sizes, and guard pages. Guard pages are no-access memory pages used to bound memory region accesses. For example, often operating systems put a guard page at the top of each thread stack to ensure references off the end of the stack region are trapped.


4. The command line arguments and properties passed in to the JNI_CreateJavaVM method are parsed and stored for later use.


5. The standard Java system properties are initialized, such as java.version, java.vendor, os.name, and so on.


6. The modules for supporting synchronization, stack, memory, and safepoint pages are initialized.


7. Libraries such as libzip, libhpi, libjava, and libthread are loaded.


8. Signal handlers are initialized and set.


9. The thread library is initialized.


10. The output stream logger is initialized.


11. Agent libraries (hprof, jdi), if any are being used, are initialized and started.


12. The thread states and the thread local storage, which holds thread specific data required for the operation of threads, are initialized.


13. A portion of the HotSpot VM global data is initialized such as the event log, OS synchronization primitives, perfMemory (performance statistics memory), and chunkPool (memory allocator).


14. At this point, the HotSpot VM can create threads. The Java version of the main thread is created and attached to the current operating system thread. However, this thread is not yet added to the known list of threads.


15. Java level synchronization is initialized and enabled.


16. bootclassloader, code cache, interpreter, JIT compiler, Java Native Interface, system dictionary, and universe are initialized.


17. The Java main thread is now added to the known list of threads. The universe, a set of required global data structures, is sanity  checked. The HotSpot VMThread, which performs all the HotSpot VM’s critical functions, is created. At this point the appropriate JVMTI events are posted to notify the current state of the HotSpot VM.


18. The following Java classes java.lang.String, java.lang.System, java.lang.Thread, java.lang.ThreadGroup, java.lang.reflect.Method, java.lang.ref.Finalizer, java.lang.Class, and the rest of the Java System classes are loaded and initialized. At this point, the HotSpot VM is initialized and operational, but not quite fully functional.

19. The HotSpot VM’s signal handler thread is started, the JIT compiler is initialized, and the HotSpot’s compile broker thread is started. Other HotSpot VM helper threads such as watcher threads and stat sampler are started. At this time the HotSpot VM is fully functional.


20. Finally, the JNIEnv is populated and returned to the caller and the HotSpot VM is ready to service new JNI requests.


对应OpenJDK中的源码:

_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
  HS_DTRACE_PROBE3(hotspot_jni, CreateJavaVM__entry, vm, penv, args);

  jint result = JNI_ERR;
  DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result);

  // We're about to use Atomic::xchg for synchronization.  Some Zero
  // platforms use the GCC builtin __sync_lock_test_and_set for this,
  // but __sync_lock_test_and_set is not guaranteed to do what we want
  // on all architectures.  So we check it works before relying on it.
#if defined(ZERO) && defined(ASSERT)
  {
    jint a = 0xcafebabe;
    jint b = Atomic::xchg(0xdeadbeef, &a);
    void *c = &a;
    void *d = Atomic::xchg_ptr(&b, &c);
    assert(a == (jint) 0xdeadbeef && b == (jint) 0xcafebabe, "Atomic::xchg() works");
    assert(c == &b && d == &a, "Atomic::xchg_ptr() works");
  }
#endif // ZERO && ASSERT

  // At the moment it's only possible to have one Java VM,
  // since some of the runtime state is in global variables.

  // We cannot use our mutex locks here, since they only work on
  // Threads. We do an atomic compare and exchange to ensure only
  // one thread can call this method at a time

  // We use Atomic::xchg rather than Atomic::add/dec since on some platforms
  // the add/dec implementations are dependent on whether we are running
  // on a multiprocessor, and at this stage of initialization the os::is_MP
  // function used to determine this will always return false. Atomic::xchg
  // does not have this problem.
  if (Atomic::xchg(1, &vm_created) == 1) {
    return JNI_ERR;   // already created, or create attempt in progress
  }
  if (Atomic::xchg(0, &safe_to_recreate_vm) == 0) {
    return JNI_ERR;  // someone tried and failed and retry not allowed.
  }

  assert(vm_created == 1, "vm_created is true during the creation");

  /**
   * Certain errors during initialization are recoverable and do not
   * prevent this method from being called again at a later time
   * (perhaps with different arguments).  However, at a certain
   * point during initialization if an error occurs we cannot allow
   * this function to be called again (or it will crash).  In those
   * situations, the 'canTryAgain' flag is set to false, which atomically
   * sets safe_to_recreate_vm to 1, such that any new call to
   * JNI_CreateJavaVM will immediately fail using the above logic.
   */
  bool can_try_again = true;

  result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
  if (result == JNI_OK) {
    JavaThread *thread = JavaThread::current();
    /* thread is thread_in_vm here */
    *vm = (JavaVM *)(&main_vm);
    *(JNIEnv**)penv = thread->jni_environment();

    // Tracks the time application was running before GC
    RuntimeService::record_application_start();

    // Notify JVMTI
    if (JvmtiExport::should_post_thread_life()) {
       JvmtiExport::post_thread_start(thread);
    }
    // Check if we should compile all classes on bootclasspath
    NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();)
    // Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
    ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native);
  } else {
    if (can_try_again) {
      // reset safe_to_recreate_vm to 1 so that retrial would be possible
      safe_to_recreate_vm = 1;
    }

    // Creation failed. We must reset vm_created
    *vm = 0;
    *(JNIEnv**)penv = 0;
    // reset vm_created last to avoid race condition. Use OrderAccess to
    // control both compiler and architectural-based reordering.
    OrderAccess::release_store(&vm_created, 0);
  }

  NOT_PRODUCT(test_error_handler(ErrorHandlerTest));
  return result;
}


原文地址:https://www.cnblogs.com/jubincn/p/3381098.html