Hotspot源码解析-第十五章-类加载器初始化前期准备

2024-01-08 19:12:52

15.1 ClassLoader初始化

15.1.1 classLoader.cpp

15.1.1.1 classLoader_init
void classLoader_init() {
  ClassLoader::initialize();
}

void ClassLoader::initialize() {
  assert(_package_hash_table == NULL, "should have been initialized by now.");
  EXCEPTION_MARK;

  if (UsePerfData) {
    // jvmstat 性能统计相关变量,这个忽略
    NEWPERFTICKCOUNTER(_perf_accumulated_time, SUN_CLS, "time");
    NEWPERFTICKCOUNTER(_perf_class_init_time, SUN_CLS, "classInitTime");
    NEWPERFTICKCOUNTER(_perf_class_init_selftime, SUN_CLS, "classInitTime.self");
    NEWPERFTICKCOUNTER(_perf_class_verify_time, SUN_CLS, "classVerifyTime");
    NEWPERFTICKCOUNTER(_perf_class_verify_selftime, SUN_CLS, "classVerifyTime.self");
    NEWPERFTICKCOUNTER(_perf_class_link_time, SUN_CLS, "classLinkedTime");
    NEWPERFTICKCOUNTER(_perf_class_link_selftime, SUN_CLS, "classLinkedTime.self");
    NEWPERFEVENTCOUNTER(_perf_classes_inited, SUN_CLS, "initializedClasses");
    NEWPERFEVENTCOUNTER(_perf_classes_linked, SUN_CLS, "linkedClasses");
    NEWPERFEVENTCOUNTER(_perf_classes_verified, SUN_CLS, "verifiedClasses");

    NEWPERFTICKCOUNTER(_perf_class_parse_time, SUN_CLS, "parseClassTime");
    NEWPERFTICKCOUNTER(_perf_class_parse_selftime, SUN_CLS, "parseClassTime.self");
    NEWPERFTICKCOUNTER(_perf_sys_class_lookup_time, SUN_CLS, "lookupSysClassTime");
    NEWPERFTICKCOUNTER(_perf_shared_classload_time, SUN_CLS, "sharedClassLoadTime");
    NEWPERFTICKCOUNTER(_perf_sys_classload_time, SUN_CLS, "sysClassLoadTime");
    NEWPERFTICKCOUNTER(_perf_app_classload_time, SUN_CLS, "appClassLoadTime");
    NEWPERFTICKCOUNTER(_perf_app_classload_selftime, SUN_CLS, "appClassLoadTime.self");
    NEWPERFEVENTCOUNTER(_perf_app_classload_count, SUN_CLS, "appClassLoadCount");
    NEWPERFTICKCOUNTER(_perf_define_appclasses, SUN_CLS, "defineAppClasses");
    NEWPERFTICKCOUNTER(_perf_define_appclass_time, SUN_CLS, "defineAppClassTime");
    NEWPERFTICKCOUNTER(_perf_define_appclass_selftime, SUN_CLS, "defineAppClassTime.self");
    NEWPERFBYTECOUNTER(_perf_app_classfile_bytes_read, SUN_CLS, "appClassBytes");
    NEWPERFBYTECOUNTER(_perf_sys_classfile_bytes_read, SUN_CLS, "sysClassBytes");


    // The following performance counters are added for measuring the impact
    // of the bug fix of 6365597. They are mainly focused on finding out
    // the behavior of system & user-defined classloader lock, whether
    // ClassLoader.loadClass/findClass is being called synchronized or not.
    // Also two additional counters are created to see whether 'UnsyncloadClass'
    // flag is being set or not and how many times load_instance_class call
    // fails with linkageError etc.
    NEWPERFEVENTCOUNTER(_sync_systemLoaderLockContentionRate, SUN_CLS,
                        "systemLoaderLockContentionRate");
    NEWPERFEVENTCOUNTER(_sync_nonSystemLoaderLockContentionRate, SUN_CLS,
                        "nonSystemLoaderLockContentionRate");
    NEWPERFEVENTCOUNTER(_sync_JVMFindLoadedClassLockFreeCounter, SUN_CLS,
                        "jvmFindLoadedClassNoLockCalls");
    NEWPERFEVENTCOUNTER(_sync_JVMDefineClassLockFreeCounter, SUN_CLS,
                        "jvmDefineClassNoLockCalls");

    NEWPERFEVENTCOUNTER(_sync_JNIDefineClassLockFreeCounter, SUN_CLS,
                        "jniDefineClassNoLockCalls");

    NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS,
                        "unsafeDefineClassCalls");

    NEWPERFEVENTCOUNTER(_isUnsyncloadClass, SUN_CLS, "isUnsyncloadClassSet");
    NEWPERFEVENTCOUNTER(_load_instance_class_failCounter, SUN_CLS,
                        "loadInstanceClassFailRate");

    // increment the isUnsyncloadClass counter if UnsyncloadClass is set.
    if (UnsyncloadClass) {
      _isUnsyncloadClass->inc();
    }
  }

  // 加载libzip.so函数库,解析出一些需要用到的函数出来,并用相应的函数指针来持有解析后的函数,方便后续使用
  load_zip_library();
#if INCLUDE_CDS
  // initialize search path
  if (DumpSharedSpaces) {
    _shared_paths_misc_info = SharedClassUtil::allocate_shared_paths_misc_info();
  }
#endif
  // 遍历class path值,并将所有classpath路径,封装成一个ClassPathEntry对象,然后形成一个链表,并由ClassLoader类的_first_entry属性指向该链表的表头,_last_entry指向表尾
  setup_bootstrap_search_path();
  if (LazyBootClassLoader) {
    // set up meta index which makes boot classpath initialization lazier
    setup_bootstrap_meta_index();
  }
}

15.1.1.2 load_zip_library

void ClassLoader::load_zip_library() {
  assert(ZipOpen == NULL, "should not load zip library twice");
  // First make sure native library is loaded
  // 首先保证本地函数库(libverify.so、libjava.so、libnet.so)已经加载
  os::native_java_library();
  // Load zip library
  char path[JVM_MAXPATHLEN];
  char ebuf[1024];
  void* handle = NULL;
  // 构建(实际上就是拼接和格式化)动态函数库完整路径文件名,这里是要构建libzip.so的完整路径文件名,路径从Arguments::get_dll_dir()函数获取,这个函数实际就是取属性sun.boot.library.path的值,这个值用户也可以在启动java时通过参数-Dsun.boot.library.path=来设置,函数库文件,如图15-2,其中图15-1展示出了日常需要使用的几个java运行时环境地址
  if (os::dll_build_name(path, sizeof(path), Arguments::get_dll_dir(), "zip")) {
    // 加载libzip.so到内存,并返回加载后的句柄,供后续符号解析使用
    handle = os::dll_load(path, ebuf, sizeof ebuf);
  }
  if (handle == NULL) {
    // 加载不到,那就是文件不存在,或者是不可读取,打印日志,并退出虚拟机
    vm_exit_during_initialization("Unable to load ZIP library", path);
  }
  // Lookup zip entry points
  // 查找libzip.so文件中以下函数符号引用,并通过类型转换成,各自函数的句柄,比如符号解析"ZIP_Open"函数后,先将解析后的结果强制类型转换成ZipOpen_t(其实就是一个函数指针),最后用ZipOpen变量来指向,其他几个函数也是依此类推,其实后续有很多这种调用动态函数库的逻辑都是经过这几步:加载函数库文件->解析符号引用->用句柄指向,大家可以把加载想像成java中import某个类
  ZipOpen      = CAST_TO_FN_PTR(ZipOpen_t, os::dll_lookup(handle, "ZIP_Open"));
  ZipClose     = CAST_TO_FN_PTR(ZipClose_t, os::dll_lookup(handle, "ZIP_Close"));
  FindEntry    = CAST_TO_FN_PTR(FindEntry_t, os::dll_lookup(handle, "ZIP_FindEntry"));
  ReadEntry    = CAST_TO_FN_PTR(ReadEntry_t, os::dll_lookup(handle, "ZIP_ReadEntry"));
  ReadMappedEntry = CAST_TO_FN_PTR(ReadMappedEntry_t, os::dll_lookup(handle, "ZIP_ReadMappedEntry"));
  GetNextEntry = CAST_TO_FN_PTR(GetNextEntry_t, os::dll_lookup(handle, "ZIP_GetNextEntry"));
  Crc32        = CAST_TO_FN_PTR(Crc32_t, os::dll_lookup(handle, "ZIP_CRC32"));

  // ZIP_Close is not exported on Windows in JDK5.0 so don't abort if ZIP_Close is NULL
  // 以下几个函数是不能为空的,否则打印错误日志,并退出虚拟机,但是ZIP_Close在jdk5的windows版本中不支持,所以这个函数为空时,不做退出处理
  if (ZipOpen == NULL || FindEntry == NULL || ReadEntry == NULL ||
      GetNextEntry == NULL || Crc32 == NULL) {
    vm_exit_during_initialization("Corrupted ZIP library", path);
  }

  // Lookup canonicalize entry in libjava.dll
  // 从libjava.so文件中解析出canonicalize函数地址,并通过CanonicalizeEntry来持有
  void *javalib_handle = os::native_java_library();
  CanonicalizeEntry = CAST_TO_FN_PTR(canonicalize_fn_t, os::dll_lookup(javalib_handle, "Canonicalize"));
  // 到此为止,libzip.so的加载已完成
}

图15-1
在这里插入图片描述

图15-2
在这里插入图片描述
在这里插入图片描述

15.1.2 os.cpp

15.1.2.1 os::native_java_library

这个函数就是加载几个函数库:libverify.so、libjava.so、libnet.so

void* os::native_java_library() {
  if (_native_java_library == NULL) {
    char buffer[JVM_MAXPATHLEN];
    char ebuf[1024];

    // 加载 libverify.so动态函数库,并装入内存
    if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(),
                       "verify")) {
      dll_load(buffer, ebuf, sizeof(ebuf));
    }

    // 加载 libjava.so动态函数库,并装入内存,dll_build_name函数就是构建要加载的函数库文件名,细节看`章节15.1.3.1`
    if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(),
                       "java")) {
      // _native_java_library 指向加载后的函数库指针,方便后续符号解析时使用
      _native_java_library = dll_load(buffer, ebuf, sizeof(ebuf));
    }
    if (_native_java_library == NULL) {
      vm_exit_during_initialization("Unable to load native library", ebuf);
    }

#if defined(__OpenBSD__)
    // 加载 libnet.so动态函数库,并装入内存
    if (dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(),
                       "net")) {
      dll_load(buffer, ebuf, sizeof(ebuf));
    }
#endif
  }
  static jboolean onLoaded = JNI_FALSE;
  if (onLoaded) {
    // We may have to wait to fire OnLoad until TLS is initialized.
    if (ThreadLocalStorage::is_initialized()) {
      // The JNI_OnLoad handling is normally done by method load in
      // java.lang.ClassLoader$NativeLibrary, but the VM loads the base library
      // explicitly so we have to check for JNI_OnLoad as well
      const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
      // 下面一段代码就是把符号解析libjava.so函数库的JNI_OnLoad函数,并执行
      JNI_OnLoad_t JNI_OnLoad = CAST_TO_FN_PTR(
          JNI_OnLoad_t, dll_lookup(_native_java_library, onLoadSymbols[0]));
      if (JNI_OnLoad != NULL) {
        JavaThread* thread = JavaThread::current();
        ThreadToNativeFromVM ttn(thread);
        HandleMark hm(thread);
        jint ver = (*JNI_OnLoad)(&main_vm, NULL);
        onLoaded = JNI_TRUE;
        if (!Threads::is_supported_jni_version_including_1_1(ver)) {
          vm_exit_during_initialization("Unsupported JNI version");
        }
      }
    }
  }
  return _native_java_library;  // 最终返回的是libjava.so的函数库指针
}

15.1.3 os_linux.cpp

15.1.3.1 dll_build_name
bool os::dll_build_name(char* buffer, size_t buflen,
                        const char* pname, const char* fname) {
  bool retval = false;
  // pname就是你上层路径
  const size_t pnamelen = pname ? strlen(pname) : 0;

  // 判断长度合法性
  if (pnamelen + strlen(fname) + 10 > (size_t) buflen) {
    return retval;
  }

  if (pnamelen == 0) {
    // 没有上层路径,直接拼接成libjava.so这种形式
    // snprintf函数内部使用到了va_start库函数,用来处理C里面的动态参数,因为C里面的动态参数只能通过栈空间的遍布来处理,这一步就把它想像成java语言里面的format方法,%s就会format成fname值
    snprintf(buffer, buflen, "lib%s.so", fname);
    retval = true;
  } else if (strchr(pname, *os::path_separator()) != NULL) {
    // 如果有上层路径并且是多个目录,就要分隔开来,每个目录都遍历
    int n;
    char** pelements = split_path(pname, &n);
    if (pelements == NULL) {
      return false;
    }
    // 遍历每个上层路径并查询路径里的函数库文件
    for (int i = 0 ; i < n ; i++) {
      // Really shouldn't be NULL, but check can't hurt
      if (pelements[i] == NULL || strlen(pelements[i]) == 0) {
        continue; // skip the empty path values
      }
      // 第一个%s格式化成当前层级路径名pelements[i],第二个%s就是要查询的函数库文件名后缀fname
      snprintf(buffer, buflen, "%s/lib%s.so", pelements[i], fname);
      if (file_exists(buffer)) {
        retval = true;
        break;
      }
    }

    // 遍历完成,释放刚才上层路径遍历时,对路径名分配的临时内存
    for (int i = 0 ; i < n ; i++) {
      if (pelements[i] != NULL) {
        FREE_C_HEAP_ARRAY(char, pelements[i], mtInternal);
      }
    }
    if (pelements != NULL) {
      FREE_C_HEAP_ARRAY(char*, pelements, mtInternal);
    }
  } else {
    // 只有一层目录,那直接按%s格式化就行
    snprintf(buffer, buflen, "%s/lib%s.so", pname, fname);
    retval = true;
  }
  return retval;
}

文章来源:https://blog.csdn.net/zhang527294844/article/details/135462638
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。