很早計劃寫篇研究JVM類載入源碼的,忙於項目一直沒時間深入研究,最近又要換工作,怕最近沒時間研究了。之前一些粗淺的研究,主要是defineClass()和loadClass()方法在JVM源碼中的幾個相關位置,作個簡單記錄。
1.defineClass
由位元組碼載入自訂類,最終歸結於java.lang.ClassLoader. defineClass0()這個native方法(關於java系統類別層面的類載入機制,參見之前的兩篇文章,本文只涉及JVM的c/c++層面源碼)。
該方法通過jni實現,對應的c代碼位於:
%openjdk-home%\jdk\src\share\native\java\lang\classload.c
實現該方法的幾層主要調用如下:
Java_java_lang_ClassLoader_defineClass1();
→JVM_DefineClassWithSource();
→jvm_define_class_common();
→SystemDictionary::resolve_from_stream();
%openjdk-home%\hotspot\src\share\vm\prims\jvm.cpp
最後一步用到的SystemDictionary這個類位於
%openjdk-home%\hotspot\src\share\vm\classfile\systemDictionary.cpp
SystemDictionary::resolve_from_stream()這個方法中,命名空間過濾部分源碼如下:
const char* pkg = "java/";
if (!HAS_PENDING_EXCEPTION &&
!class_loader.is_null() &&
!parsed_name.is_null() &&
!strncmp((const char*)parsed_name->bytes(), pkg, strlen(pkg))) {
// It is illegal to define classes in the "java." package from
// JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
ResourceMark rm(THREAD);
char* name = parsed_name->as_C_string();
char* index = strrchr(name, '/');
*index = '\0'; // chop to just the package name
while ((index = strchr(name, '/')) != NULL) {
*index = '.'; // replace '/' with '.' in package name
}
const char* fmt = "Prohibited package name: %s";
size_t len = strlen(fmt) + strlen(name);
char* message = NEW_RESOURCE_ARRAY(char, len);
jio_snprintf(message, len, fmt, name);
Exceptions::_throw_msg(THREAD_AND_LOCATION,
vmSymbols::java_lang_SecurityException(), message);
}
高亮的幾行代碼可以看出:如類名以java.開頭,將拋出java.lang.SecurityException異常。之前。之前嘗試用自訂的方式載入系統類別時,發生異常,根源就在這裡。
理論上,刪除或修改這段代碼,就可以實現自訂系統類別載入。不過編譯JVM本身就是比較複雜的工作,至今尚未實現過~~~
2.loadClass
那麼以java.開頭java系統類別是如何?載入的?其流程與上面分析的基本類似。這裡不再詳細敘述,只給出幾個關鍵的方法:
(native方法) java.lang.Classload.findBootstrapClass()
→(classload.c) Java_java_lang_ClassLoader_findBootstrapClass
→(jvm.cpp) find_class_from_class_loader()
→(systemDictionary.cpp) resolve_instance_class_or_null()
→(java.lang.Classload.class)checkPackageAccess
→(java.lang.SecurityManager.class)checkPackageAccess
與之前有所不同的是安全檢查是通過JNI反調java方法實現的