Hotspot启动和初始化源码解析

   目录

1、JVM启动入口

2、JLI_Launch

3、可移植性

4、JavaMain

 5、JavaVM和JNIEnv对象

 6、JNI_CreateJavaVM

7、Threads::create_vm


      学习一个复杂项目源码的最关键的一步是找准应用启动和对外提供服务的入口,从这些入口处开始顺藤摸瓜式的查看代码,可以对项目的各功能模块有一个整体宏观上的认识,并以此为切入点,有的放矢,按需深入了解各功能模块的实现细节,这是最高效的学习源码的方式。JVM的启动入口在哪了?可以借助GDB的start命令查看。

1、JVM启动入口

     OpenJDK的源码下载和编译可以参考Java程序员自我修养——OpenJDK8 编译,调试和目录结构,编译完成可以写一个HelloWorld.java作为测试代码,如下:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

将其保存在编译后生成/build/linux-x86_64-normal-server-slowdebug/jdk/bin/目录下,执行./javac HelloWorld.java将其编译成class文件,执行./java HelloWorld,程序正确打印则编译成功。执行gdb -q ./java进入gdb调试,执行set args HelloWorld设置启动参数,即执行./java的参数,执行start命令,gdb停在启动入口处,如下图:

启动入口在/home/openjdk/openjdk-jdk8u-master/jdk/src/share/bin/main.c:97,查看代码可知其核心启动方法是JLI_Launch。

2、JLI_Launch

     其主要流程如下:

  1. SelectVersion,从jar包中manifest文件或者命令行读取用户使用的JDK版本,判断当前版本是否合适
  2. CreateExecutionEnvironment,设置执行环境参数
  3. LoadJavaVM,加载libjvm动态链接库,从中获取JNI_CreateJavaVM,JNI_GetDefaultJavaVMInitArgs和JNI_GetCreatedJavaVMs三个函数的实现,其中JNI_CreateJavaVM是JVM初始化的核心入口,具体实现在hotspot目录中
  4. ParseArguments 解析命令行参数,如-version,-help等参数在该方法中解析的
  5. SetJavaCommandLineProp 解析形如-Dsun.java.command=的命令行参数
  6. SetJavaLauncherPlatformProps 解析形如-Dsun.java.launcher.*的命令行参数
  7. JVMInit,通过JVMInit->ContinueInNewThread->ContinueInNewThread0->pthread_create创建了一个新的线程,执行JavaMain函数,主线程pthread_join该线程,在JavaMain函数中完成虚拟机的初始化和启动。

 执行export _JAVA_LAUNCHER_DEBUG=1设置环境变量,然后可以查看整个启动过程的日志,如下图:

Hotspot启动和初始化源码解析_第1张图片

3、可移植性

     如点击LoadJavaVM时,eclipse弹框如下:

Hotspot启动和初始化源码解析_第2张图片

    该函数的实现到底是哪个文件了?为啥没有Linux的实现了?Linux和solaris都是类Unix系统,所以这里应该是共用solaris的实现,怎么证明了?用eclipse 全局搜索包含java_md_solinux.c文件名的文件,其中位于jdk目录下的只有一个文件,CoreLibraries.gmk,如下图:

Hotspot启动和初始化源码解析_第3张图片

如果不是macosx和windows则执行BUILD_LIBJLI_FILES += java_md_solinux.c ergo.c,即将  java_md_solinux.c这个文件加入到编译路径中,从而使用java_md_solinux.c的实现。除通过这种构建脚本的方式实现各操作系统兼容外,JVM还使用了宏定义和预处理的方式,如java_md.h中有一段代码,如下:

Hotspot启动和初始化源码解析_第4张图片

当定义了宏MACOSX以后,就包含java_md_macosx.h,否则包含java_md_solinux.h,这两个头文件包含了特定于该操作系统的特殊头文件和变量定义。类似这种代码在hotspot目录下特别常见,JVM通常将公共的代码放在share目录下,特定于操作系统的代码放在该操作系统的目录下,然后通过上述两种方式在编译打包时根据构建脚本配置或者宏定义编译打包成特定于操作系统的JDK,如下图:

Hotspot启动和初始化源码解析_第5张图片

这类宏是执行配置检查的configure根据所在的操作系统的特点在编译时自动注入进去,但是eclipse没有执行configure这个过程,需要给编译器添加宏定义,从而让预处理器能够正确的包含头文件。添加方法是,在C++的工程上点击右键,选择properties,然后如下图操作点击Add:

Hotspot启动和初始化源码解析_第6张图片

接着如下图,添加宏定义:

Hotspot启动和初始化源码解析_第7张图片

点击OK,然后Apply and Close后编译器自动重新执行代码的预处理,引入正确的头文件。

4、JavaMain

     JLI_Launch作为启动器,创建了一个新线程执行JavaMain函数,JLI_Launch所在的线程称为启动线程,执行JavaMain函数的称之为Main线程。JavaMain函数的主要流程如下:

  1. InitializeJVM 初始化JVM,给JavaVM和JNIEnv对象正确赋值,通过调用InvocationFunctions结构体下的CreateJavaVM方法指针实现,该指针在LoadJavaVM方法中指向libjvm动态链接库中JNI_CreateJavaVM函数。
  2. LoadMainClass 获取应用程序的MainClass,即包含java程序启动入口main方法的类,
  3. GetApplicationClass  JavaFX没有MainClass而是通过ApplicationClass启动的,这里获取ApplicationClass
  4. PostJVMInit  将ApplicationClass作为应用名传给JavaFX本身,比如作为主菜单
  5. (*env)->GetStaticMethodID  获取main方法的方法ID
  6. CreateApplicationArgs  解析main方法的参数
  7. (*env)->CallStaticVoidMethod  执行main方法
  8. LEAVE main方法执行完毕,JVM退出,包含两步,(*vm)->DetachCurrentThread,让当前Main线程同启动线程断联,然后创建一个新的名为DestroyJavaVM的线程,让该线程等待所有的非后台进程退出,并在最后执行(*vm)->DestroyJavaVM方法。

 5、JavaVM和JNIEnv对象

      点击JavaVM,eclipse弹框如下:

Hotspot启动和初始化源码解析_第8张图片

    在4个文件中都有定义,同一个文件中有两处定义,以jdk下面的目录为例,如下图:

Hotspot启动和初始化源码解析_第9张图片

    __cplusplus宏表示当前系统支持C++,通常服务器操作系统如CentOS都支持C++,只有嵌入式这类微型操作系统不支持C++,纯C开发,我们只关注C++下的JavaVM实现即可,进一步查看两者的具体定义可知,JavaVM_只是对JNIInvokeInterface_下的方法指针做了一个包装,将其转换成类方法而已,如下:

Hotspot启动和初始化源码解析_第10张图片

而JNIInvokeInterface_结构体就只有方法指针,reserved0等保留的方法,如下:

Hotspot启动和初始化源码解析_第11张图片

进一步查看位于hotspot目录下的JavaVM的定义发现同jdk目录下的定义完全一样,初步判断jdk中JavaVM定义相当于接口,具体的实现在hotspot目录下,两者是隔离的src目录所以有两份定义的头文件。

JNIEnv对象同JavaVM对象,两者都是在同一个头文件jni.h中定义,定义方式一样,JNIEnv定义的方法更多,如下图:

Hotspot启动和初始化源码解析_第12张图片

根据方法命名推断其功能可知JNIEnv是java代码在JVM正常执行的核心,包含字节码指令的解释和同底层操作系统的交互等,JavaVM主要用于JVM退出。

 6、JNI_CreateJavaVM

      JavaVM和JNIEnv对象的正确赋值和JVM各模块的初始化都是在JNI_CreateJavaVM函数中完成,这个函数是从libjvm动态链接库中获取的,那么他在代码中对应哪个函数了?可以在头文件jni.h中点击该方法,eclipse弹框如下:

Hotspot启动和初始化源码解析_第13张图片

我们只考虑share目录下的通用实现,主要流程如下

_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {

  //通过Atomic::xchg方法修改全局volatile变量vm_created为1,该变量默认为0,如果返回1则说明JVM已经创建完成或者创建中,返回JNI_EEXIST错误码,如果返回0则说明JVM未创建
  if (Atomic::xchg(1, &vm_created) == 1) {
    return JNI_EEXIST;   // already created, or create attempt in progress
  }
  //通过Atomic::xchg方法修改全局volatile变量safe_to_recreate_vm为0,该变量默认为1,如果返回0则说明JVM已经在重新创建了,返回JNI_ERR错误码,如果返回1则说明JVM未创建
  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");
  
  bool can_try_again = true;
  //完成JVM的初始化,如果初始化过程中出现不可恢复的异常则can_try_again会被置为false
  result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
  //初始化正常
  if (result == JNI_OK) {
    //获取当前线程,即执行create_vm的线程,也是JNI_CreateJavaVM执行完毕后执行main方法的线程
    JavaThread *thread = JavaThread::current();
    /*JavaVM赋值,main_vm是jni.h中的全局变量,最终实现指向全局变量jni_InvokeInterface */
    *vm = (JavaVM *)(&main_vm);
    //JNIEnv赋值
    *(JNIEnv**)penv = thread->jni_environment();
    // 记录应用的启动时间
    RuntimeService::record_application_start();
    // 通知JVMTI应用启动
    if (JvmtiExport::should_post_thread_life()) {
       JvmtiExport::post_thread_start(thread);
    }

    EventThreadStart event;
    if (event.should_commit()) {
      event.set_javalangthread(java_lang_Thread::thread_id(thread->threadObj()));
      event.commit();
    }

    //根据配置加载类路径中所有的类
    if (CompileTheWorld) ClassLoader::compile_the_world();
    if (ReplayCompiles) ciReplay::replay(thread);

    // win* 系统添加异常处理的wrapper
    CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(test_error_handler);
    CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(execute_internal_vm_tests);

    //设置当前线程的线程状态
    ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native);
  } else {
  //如果create_vm初始化失败
    if (can_try_again) {
      // 如果可以重试则恢复默认值1
      safe_to_recreate_vm = 1;
    }

    //将vm和penv置空,vm_created重置成0
    *vm = 0;
    *(JNIEnv**)penv = 0;
    OrderAccess::release_store(&vm_created, 0);
  }

  return result;
}

上述代码解释了JavaVM如何初始化的,该对象直接指向jni.cpp中的全局变量main_vm,该变量通过全局变量jni_InvokeInterface完成初始化,jni_InvokeInterface的定义如下:

Hotspot启动和初始化源码解析_第14张图片

7、Threads::create_vm

    点击create_vm就可直接跳转到该方法的实现,位于\hotspot\src\share\vm\runtime\目录下的thread.cpp中,整个流程比较复杂,如下:

jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {

  //预初始化,可以由特定平台按需覆盖定义,默认为空实现
  VM_Version::early_initialize();

  //检查当前JDK版本是否支持代码编译的JDK版本
  if (!is_supported_jni_version(args->version)) return JNI_EVERSION;

  //流模块的初始化
  ostream_init();

  //处理启动器相关参数,如-Dsun.java.launcher
  Arguments::process_sun_java_launcher_properties(args);

  //OS模块的初始化,包含设置内存页大小,获取物理内存大小,锁的初始化
  os::init();

  //初始化系统属性,所有的属性通过Arguments类的静态属性_system_properties保存,该属性是一个SystemProperty类的指针
  //每个属性对应一个SystemProperty实例,通过链表的形式关联起来
  Arguments::init_system_properties();

  //初始JDK_Version类的静态变量_current,获取当前JDK的版本信息,如_major主版本号,_minor次版本号,_update更新时间等
  JDK_Version_init();

  // 初始特定jdk版本的系统属性
  Arguments::init_version_specific_system_properties();

  //解析命令行参数
  jint parse_result = Arguments::parse(args);
  if (parse_result != JNI_OK) return parse_result;
  //初始化可用CPU核数,根据配置设置大内存页
  os::init_before_ergo();
  //设置系统参数,如是否使用指针压缩,堆内存大小,GC参数等
  jint ergo_result = Arguments::apply_ergo();
  if (ergo_result != JNI_OK) return ergo_result;

  if (PauseAtStartup) {
  //解析启动文件
    os::pause();
  }

  //记录启动时间
  TraceVmCreationTime create_vm_timer;
  create_vm_timer.start();

  // 打印启动时间
  TraceTime timer("Create VM", TraceStartupTime);

  //OS模块的二次初始化,包含线程锁的初始,polling_page和mem_serialize_page分配内存,信号处理初始化,设置线程栈大小,libpthread初始化,
  //设置线程锁,线程优先级策略的初始化
  jint os_init_2_result = os::init_2();
  if (os_init_2_result != JNI_OK) return os_init_2_result;
 //UseNUMA时额外的参数设置
  jint adjust_after_os_result = Arguments::adjust_after_os();
  if (adjust_after_os_result != JNI_OK) return adjust_after_os_result;

  // 初始化TLS
  ThreadLocalStorage::init();

  //初始化GC日志和LoadedClass日志的fileStream对象
  ostream_init_log();

  // 将参数-Xrun 转换成 -agentlib参数
  // Must be before create_vm_init_agents()
  if (Arguments::init_libraries_at_startup()) {
    convert_vm_init_libraries_to_agents();
  }

  // 根据-agentlib/-agentpath and 转换后 -Xrun参数创建agents
  if (Arguments::init_agents_at_startup()) {
    create_vm_init_agents();
  }

  // Initialize Threads state
  _thread_list = NULL;
  _number_of_threads = 0;
  _number_of_non_daemon_threads = 0;

  // 初始化Events,各种锁,ChunkPool,PerfMemory
  vm_init_globals();

  //创建一个新的JavaThread并设置相关属性,注意这里并未创建一个新的线程,而是将当前线程同JavaThread对象关联起来,通过JavaThread管理相关属性
  JavaThread* main_thread = new JavaThread();
  main_thread->set_thread_state(_thread_in_vm);
  main_thread->record_stack_base_and_size();
  main_thread->initialize_thread_local_storage();
  main_thread->set_active_handles(JNIHandleBlock::allocate_block());

  if (!main_thread->set_as_starting_thread()) {
    vm_shutdown_during_initialization(
      "Failed necessary internal allocation. Out of swap space");
    delete main_thread;
    *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
    return JNI_ENOMEM;
  }
  main_thread->create_stack_guard_pages();

  //ObjectMonitor初始化
  ObjectMonitor::Initialize() ;

  //全局模块初始化,如Management模块,Bytecodes模块,ClassLoader模块,CodeCache模块,StubRoutines模块,Universe模块,Interpreter模块等
  jint status = init_globals();
  if (status != JNI_OK) {
    delete main_thread;
    *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
    return status;
  }

  // Should be done after the heap is fully created
  main_thread->cache_global_variables();

  HandleMark hm;

  { MutexLocker mu(Threads_lock);
    Threads::add(main_thread);
  }

//将加载时创建的raw monitors转换成真正的raw monitor
  JvmtiExport::transition_pending_onload_raw_monitors();

  // 创建VMThread
  { TraceTime timer("Start VMThread", TraceStartupTime);
    VMThread::create();
    Thread* vmthread = VMThread::vm_thread();

    if (!os::create_thread(vmthread, os::vm_thread))
      vm_exit_during_initialization("Cannot create VM thread. Out of system resources.");

    //等待VMThread初始化完成
    {
      MutexLocker ml(Notify_lock);
      os::start_thread(vmthread);
      while (vmthread->active_handles() == NULL) {
        Notify_lock->wait();
      }
    }
  }

  assert (Universe::is_fully_initialized(), "not initialized");
  if (VerifyDuringStartup) {
    //验证VMThread的状态
    VM_Verify verify_op;
    VMThread::execute(&verify_op);
  }

  EXCEPTION_MARK;

 
  if (DumpSharedSpaces) {
  //Dump元空间
    MetaspaceShared::preload_and_dump(CHECK_0);
    ShouldNotReachHere();
  }

  //标记JVMTI启用
  JvmtiExport::enter_start_phase();

  //通知JVMTI agents虚机已启动
  JvmtiExport::post_vm_start();
  //加载核心Java类文件
  {
    TraceTime timer("Initialize java.lang classes", TraceStartupTime);

    if (EagerXrunInit && Arguments::init_libraries_at_startup()) {
      create_vm_init_libraries();
    }

    initialize_class(vmSymbols::java_lang_String(), CHECK_0);

    // Initialize java_lang.System (needed before creating the thread)
    initialize_class(vmSymbols::java_lang_System(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0);
    Handle thread_group = create_initial_thread_group(CHECK_0);
    Universe::set_main_thread_group(thread_group());
    initialize_class(vmSymbols::java_lang_Thread(), CHECK_0);
    oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0);
    //将main_thread同Java中的thread_object关联起来
    main_thread->set_threadObj(thread_object);
    // 设置thread_object的线程状态
    java_lang_Thread::set_thread_status(thread_object,
                                        java_lang_Thread::RUNNABLE);

    // The VM creates & returns objects of this class. Make sure it's initialized.
    initialize_class(vmSymbols::java_lang_Class(), CHECK_0);

    // The VM preresolves methods to these classes. Make sure that they get initialized
    initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ref_Finalizer(),  CHECK_0);
    call_initializeSystemClass(CHECK_0);

    // get the Java runtime name after java.lang.System is initialized
    JDK_Version::set_runtime_name(get_java_runtime_name(THREAD));
    JDK_Version::set_runtime_version(get_java_runtime_version(THREAD));

    // an instance of OutOfMemory exception has been allocated earlier
    initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK_0);
    initialize_class(vmSymbols::java_lang_NullPointerException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ClassCastException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ArrayStoreException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_ArithmeticException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK_0);
    initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK_0);
    initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK_0);
  }

  initialize_class(vmSymbols::java_lang_Compiler(), CHECK_0);

  
  //将java.vm.info设置成nojit
  reset_vm_info_property(CHECK_0);

  //初始化JNIEnv中GetField相关接口
  quicken_jni_functions();

  // Must be run after init_ft which initializes ft_enabled
  if (TRACE_INITIALIZE() != JNI_OK) {
    vm_exit_during_initialization("Failed to initialize tracing backend");
  }

  // 设置初始化完成标识
  set_init_completed();
   //元空间的预初始化
  Metaspace::post_initialize();


  
#if INCLUDE_MANAGEMENT
// 记录初始化完成时间
  Management::record_vm_init_completed();
#endif

  // 完成SystemClassLoader的加载
  SystemDictionary::compute_java_system_loader(THREAD);
  if (HAS_PENDING_EXCEPTION) {
    vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
  }

#if INCLUDE_ALL_GCS
  //并发标记的初始化,会创建一个新的执行标记的线程
  if (UseConcMarkSweepGC || UseG1GC) {
    if (UseConcMarkSweepGC) {
      ConcurrentMarkSweepThread::makeSurrogateLockerThread(THREAD);
    } else {
      ConcurrentMarkThread::makeSurrogateLockerThread(THREAD);
    }
    if (HAS_PENDING_EXCEPTION) {
      vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
    }
  }
#endif // INCLUDE_ALL_GCS

  //JVMTI打标,标识JVM进入可用状态
  JvmtiExport::enter_live_phase();

  //创建一个新的线程处理信号
  os::signal_init();


  if (!DisableAttachMechanism) {
    //移除.java_pid文件,准备好初始化
    AttachListener::vm_start();
    if (StartAttachListener || AttachListener::init_at_startup()) {
      //初始化
      AttachListener::init();
    }
  }

  
  if (!EagerXrunInit && Arguments::init_libraries_at_startup()) {
     //加载-Xrun执行的agent 库
    create_vm_init_libraries();
  }

  //通知JVMTI JVM初始化完成
  JvmtiExport::post_vm_initialized();

  if (TRACE_START() != JNI_OK) {
    vm_exit_during_initialization("Failed to start tracing backend.");
  }

  if (CleanChunkPoolAsync) {
     //开启一个新的线程用于清理ChunkPool
    Chunk::start_chunk_pool_cleaner_task();
  }

  // c1 和 c2编译器初始化,会创建编译线程
#if defined(COMPILER1) || defined(COMPILER2) || defined(SHARK)
  CompileBroker::compilation_init();
#endif

  //加载动态代理相关类
  if (EnableInvokeDynamic) {
    initialize_class(vmSymbols::java_lang_invoke_MethodHandle(), CHECK_0);
    initialize_class(vmSymbols::java_lang_invoke_MemberName(), CHECK_0);
    initialize_class(vmSymbols::java_lang_invoke_MethodHandleNatives(), CHECK_0);
  }

#if INCLUDE_MANAGEMENT
   //加载Management相关类
  Management::initialize(THREAD);
#endif 

  if (HAS_PENDING_EXCEPTION) {
    //management加载失败
    vm_exit(1);
  }

//初始化监控任务线程
  if (Arguments::has_profile())       FlatProfiler::engage(main_thread, true);
  if (MemProfiling)                   MemProfiler::engage();
  StatSampler::engage();
  if (CheckJNICalls)                  JniPeriodicChecker::engage();
  //偏向锁的初始化
  BiasedLocking::init();

#if INCLUDE_RTM_OPT
  RTMLockingCounters::init();
#endif

  if (JDK_Version::current().post_vm_init_hook_enabled()) {
    //回调代码中的钩子函数,通知其JVM初始化完成
    call_postVMInitHook(THREAD);
    if (HAS_PENDING_EXCEPTION) {
      CLEAR_PENDING_EXCEPTION;
    }
  }

  {
      MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
      // 确保WatcherThread可以通过start()的形式启动
      WatcherThread::make_startable();
      // 如果有任何周期的监控任务则启动监控线程
      if (PeriodicTask::num_tasks() > 0) {
          WatcherThread::start();
      }
  }
  //标记JVM启动完成
  create_vm_timer.end();
#ifdef ASSERT
  _vm_complete = true;
#endif
  return JNI_OK;
}

  其中JNIEnv对象的初始化在JavaThread* main_thread = new JavaThread()时完成,JavaThread()这里调用JavaThread(bool is_attaching_via_jni = false)的构造方法,然后执行initialize()-》set_jni_functions(jni_functions());完成初始化,jni_functions()方法返回thread.cpp中的一个全局变量,如下图:

Hotspot启动和初始化源码解析_第15张图片

该变量的赋值如下:

Hotspot启动和初始化源码解析_第16张图片

 set_jni_functions()函数的实现如下:

_jni_environment就是main_thread保留的JNIEnv对象了,正确设置functions属性就表示该对象初始化完成。

到此为止,JVM的初始化就完成了,其核心就在create_vm这个方法中,至于各子模块的初始化细节在后续的子模块讲解中再逐一探讨。

你可能感兴趣的:(Hotspot和Linux内核,JVM启动,JVM初始化,JavaVM,JNIEnv)