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();
}
}
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
这个函数就是加载几个函数库: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的函数库指针
}
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;
}