類的雙親委派機制


前一篇介紹了3種類加載器,每種類加載器都加載指定路徑下的類庫,它們在具體使用時並不是相互獨立的,而是相互配合對類進行加載。另外如果有必要,還可以編寫自定義的類加載器。這些類加載器的的關系一般如下圖所示。

 

上圖的雙親委派模型中的各個類加載器之間並不表示繼承關系,而是表示工作過程,具體說就是,對於一個加載類的具體請求,首先要委派給自己的父類去加載,只有當父類無法完成加載請求時,子類自己才會去嘗試加載。類加載器加載類時,不會涉及到類的連接、初始化等步驟,僅將Class文件中的可用信息轉換為C++中對應的Klass實例存儲。類加載器具體的委派邏輯在java.lang.ClassLoader類的loadClass()方法中實現。loadClass()方法的實現如下:

源代碼位置:java/lang/ClassLoader.java
protected Class<?> loadClass(Stringname,boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 判斷此類是否已經加載過 Class c = findLoadedClass(name); // (1) if (c ==null) { try { // 委托給父類加載器進行加載 if (parent !=null) { c = parent.loadClass(name,false); (2) } else { // 父類加載器為null,委托給啟動類加載器加載 c = findBootstrapClassOrNull(name); (3) } } catch (ClassNotFoundException) { // 如果父類加載器拋出ClassNotFoundException異常,表明父類無法完成加載請求 } if (c ==null) { // 若仍然沒有找到則調用findClass()方法查找 c = findClass(name); (4) ... } } if (resolve) { resolveClass(c); //(5) } return c; } }

類的加載流程如下圖所示。

 

代碼首先通過調用findLoadedClass()方法查找此類是否已經被加載過了,如果沒有,則需要優先調用父類加載器去加載。除了用C++實現的引導類加載器需要通過調用findBootstrapClassOrNull()方法外,其它用Java實現的類加載器都通過parent字段查找父類加載器。因為這些類都繼承了java.lang.ClassLoader這個基類(這個類中有對parent字段的定義),如實現了擴展類加載器的ExtClassLoader類和實現了應用類加載器/系統類加載器的AppClassLoader類的繼承關系如下圖所示。

 

當父類無法實現加載請求時,也就是c為null時,當前類加載器調用findClass()方法嘗試自己完成加載請求。

編寫一個自定義的類加載器,如下:

實例1

package com.jvm;

import java.net.URL;
import java.net.URLClassLoader;

public class UserClassLoader extends URLClassLoader {
	 
    public UserClassloader(URL[] urls) {
        super(urls);
    }
 
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        return super.loadClass(name, resolve);
    }
}

可以看到UserClassLoader繼承了URLClassLoader類並覆寫了loadClass()方法,調用super.loadClass()方法其實就是在調用ClassLoader類中實現的loadClass()方法。

實例1(續)

package com.jvm;

public class Student { }

實例1(續)

package com.jvm;

import java.net.URL;

public class TestClassLoader {
	public static void main(String[] args) throws Exception {
		URL url[] = new URL[1];
		url[0] = Thread.currentThread().getContextClassLoader().getResource("");

		UserClassLoader ucl = new UserClassLoader(url);
		Class clazz = ucl.loadClass("com.jvm.Student");

		Object obj = clazz.newInstance();
	}
}

通過UserClassLoader類加載器加載Student類並通過調用Class.newInstance()方法獲取Student對象。

下面詳細介紹loadClass()方法中調用的findLoaderClass()、findBootstrapClassOrNull()與findClass()方法的實現。

1.findLoadedClass()方法

方法用來判斷此類是否已經加載過。調用的findLoadedClass()方法的實現如下: 

protected final Class<?> findLoadedClass(String name) {
        return findLoadedClass0(name);
}

調用本地方法findLoadedClass0()方法,這個方法的實現如下:

源代碼位置:hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name)) JVMWrapper("JVM_FindLoadedClass"); // THREAD表示當前線程 ResourceMark rm(THREAD); Handle h_name (THREAD, JNIHandles::resolve_non_null(name)); // 獲取類名對應的Handle Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL); // 檢查是否為空 const char* str = java_lang_String::as_utf8_string(string()); if (str == NULL) return NULL; // 判斷類名是否超長 const int str_len = (int)strlen(str); if (str_len > Symbol::max_length()) { return NULL; } // 創建一個臨時的Symbol TempNewSymbol klass_name = SymbolTable::new_symbol(str, str_len, CHECK_NULL); // 獲取類加載器對應的Handle Handle h_loader(THREAD, JNIHandles::resolve(loader)); // 查找目標類是否存在 Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,h_loader,Handle(),CHECK_NULL); // 將Klass轉換成jclass return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror()); JVM_END

JVM_ENTRY是宏定義,用於處理JNI調用的預處理,如獲取當前線程的JavaThread指針。因為GC等原因,JNI函數不能直接訪問Klass、oop對象,只能借助jobject、jclass等來訪問,所以會調用JNIHandles::resolve_non_null()、JNIHandles::resolve()與JNIHandles::mark_local()等函數進行轉換。

調用的find_instance_or_array_klass()函數的實現如下: 

源代碼位置:hotspot/src/share/vm/classfile/systemDicitonary.cpp

// Look for a loaded instance or array klass by name.  Do not do any loading.
// return NULL in case of error.
Klass* SystemDictionary::find_instance_or_array_klass(
  Symbol*   class_name,
  Handle    class_loader,
  Handle    protection_domain,
  TRAPS
){

  Klass* k = NULL;

  assert(class_name != NULL, "class name must be non NULL");

  if (FieldType::is_array(class_name)) { // 數組的查找邏輯
    // The name refers to an array.  Parse the name.
    // dimension and object_key in FieldArrayInfo are assigned as a side-effect of this call
    FieldArrayInfo fd;
    BasicType t = FieldType::get_array_info(class_name, fd, CHECK_(NULL));
    if (t != T_OBJECT) {
        k = Universe::typeArrayKlassObj(t);
    } else {
        Symbol* sb = fd.object_key();
        k = SystemDictionary::find(sb, class_loader, protection_domain, THREAD);
    }
    if (k != NULL) {
    	// k是個表示類的InstanceKlass對象或表示是一維數組的基本類型數組,而class_name可能表示的是多維數組,
    	// 所以還可能需要根據維度創建出ObjArrayKlass對象
        k = k->array_klass_or_null(fd.dimension());
    }
  } else {  // 類的查找邏輯
      k = find(class_name, class_loader, protection_domain, THREAD);
  }
  return k;
}

其中有對數組和類的查詢邏輯,方法並不涉及類的加載。如果是數組,首先要找到組成數組的基本元素的類型t;如果基本元素的類型是是基本類型,調用Universe::typeArrayKlassObj()函數找到表示基本類型的Klass對象,如果基本元素的類型是對象,調用SystemDictionary::find()方法查找。

調用的Universe::typeArrayKlassObj()函數是從Universe::_typeArrayKlassObjs數組中獲取Klass*。在之前介紹HotSpot的類模型時以boolean類型的一維數組創建TypeArrayKlass為例介紹過基本一維數組的創建過程,創建完成后會存儲到Universe::_typeArrayKlassObjs數組中。

SystemDictionary類中的find()方法的實現如下:

源代碼位置:hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::find(
  Symbol*  class_name,
  Handle   class_loader,
  Handle   protection_domain,
  TRAPS
) {

  // ...
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
  ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());

  // ...
  unsigned int  d_hash = dictionary()->compute_hash(class_name, loader_data);
  int           d_index = dictionary()->hash_to_index(d_hash);

  {
    // ... 
    return dictionary()->find(d_index, d_hash, class_name, loader_data,protection_domain, THREAD);
  }
}

將已經加載的類存儲在Dictionary中,為了加快查找采用了hash存儲。只有類加載器和類才能確定唯一的表示Java類的Klass實例,所以在計算d_hash時必須傳入class_name和loader_data這兩個參數。計算出具體索引d_index后,就可以調用Dictionary類的find()方法進行查找了。調用的Dictionary::find()函數的實現如下:

Klass* Dictionary::find(
	int                index,
	unsigned int       hash,
	Symbol*            name,
	ClassLoaderData*   loader_data,
	Handle             protection_domain,
	TRAPS
) {
  DictionaryEntry* entry = get_entry(index, hash, name, loader_data);
  if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {
    return entry->klass();
  } else {
    return NULL;
  }
}

調用get_entry()函數從hash表中查找實體,如果找到並且驗證是合法的,則返回Klass對象,否則返回NULL。 

2.findBootstrapClassOrNull()方法

調用findBootstrapClassOrNull()方法請求引導類加載器完成類加載請求,這個方法會調用本地方法findBootstrapClass()方法,源代碼如下: 

private Class<?> findBootstrapClassOrNull(String name){
    return findBootstrapClass(name);
}

// return null if not found
private native Class<?> findBootstrapClass(String name);

這個本地方法在HotSpot中的實現如下: 

源代碼位置:/src/share/native/java/lang/ClassLoader.c 

JNIEXPORT jclass JNICALL Java_java_lang_ClassLoader_findBootstrapClass(
   JNIEnv     *env, 
   jobject    loader,
   jstring    classname
){
    jclass cls = 0;
    // ...
    cls = JVM_FindClassFromBootLoader(env, clname);
    // ...
    return cls;
}

調用JVM_FindClassFromBootLoader()函數查找啟動類加載器加載的類,如果沒有查找到,方法會返回NULL。函數的實現如下:

源代碼位置:hotspot/src/share/vm/prims/jvm.cpp

// Returns a class loaded by the bootstrap class loader; or null
// if not found.  ClassNotFoundException is not thrown.

JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,const char* name))

  // Java libraries should ensure that name is never null...
  if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    return NULL;
  }

  TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
  Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
  if (k == NULL) {
    return NULL;
  }

  return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END
  

調用SystemDictionary::resolve_or_null()函數對類進行查找,函數的實現如下: 

Klass* SystemDictionary::resolve_or_null(Symbol* class_name, TRAPS) {
  return resolve_or_null(class_name, Handle(), Handle(), THREAD);
}

Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
  // 數組,通過簽名的格式來判斷
  if (FieldType::is_array(class_name)) {
    return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  }
  // 普通類,通過簽名的格式來判斷
  else if (FieldType::is_obj(class_name)) {
    ResourceMark rm(THREAD);
    // 去掉簽名中的開頭字符L和結束字符;
    TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                                 class_name->utf8_length() - 2,
						 CHECK_NULL);
    return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
  }
  else {
    return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  }
}

調用resolve_array_class_or_null()方法查找數組時,如果組成數組元素的基本類型為引用類型,同樣會調用resolve_instance_class_or_null()方法來查找類對應的Klass實例。方法的實現如下:

Klass* SystemDictionary::resolve_array_class_or_null(
   Symbol*   class_name,
   Handle    class_loader,
   Handle    protection_domain,
   TRAPS
){
  assert(FieldType::is_array(class_name), "must be array");
  Klass*          k = NULL;
  FieldArrayInfo  fd;
  // dimension and object_key in FieldArrayInfo are assigned as a side-effect of this call
  BasicType t = FieldType::get_array_info(class_name, fd, CHECK_NULL);
  if (t == T_OBJECT) {
    // naked oop "k" is OK here -- we assign back into it
	Symbol* sb = fd.object_key();
    k = SystemDictionary::resolve_instance_class_or_null(sb,class_loader,protection_domain,CHECK_NULL);
    if (k != NULL) {
       k = k->array_klass(fd.dimension(), CHECK_NULL);
    }
  } else {
    k = Universe::typeArrayKlassObj(t);
    int x = fd.dimension();
    TypeArrayKlass* tak = TypeArrayKlass::cast(k);
    k = tak->array_klass(x, CHECK_NULL);
  }
  return k;
}

包含對元素類型為引用類型和元素類型為一維數組的基本類型的Klass實例的查找。一維數組的基本類型的查找和find_instance_or_array_klass()方法的實現基本類似。下面看調用的resolve_instance_class_or_null()方法對引用類型的查找,實現如下: 

源代碼位置:hotspot/src/share/vm/classfile/systemDictionary.cpp

Klass* SystemDictionary::resolve_instance_class_or_null(
   Symbol*   name,
   Handle    class_loader,
   Handle    protection_domain,
   TRAPS 
){

  // UseNewReflection
  // Fix for 4474172; see evaluation for more details
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
  ClassLoaderData  *loader_data = register_loader(class_loader, CHECK_NULL);

  // Do lookup to see if class already exist and the protection domain has the right access
  // This call uses find which checks protection domain already matches
  // All subsequent calls use find_class, and set has_loaded_class so that
  // before we return a result we call out to java to check for valid protection domain
  // to allow returning the Klass* and add it to the pd_set if it is valid
  // 在變量SystemDictionary::_dictionary中查找是否類已經加載,如果加載就直接返回
  Dictionary* dic = dictionary();
  // 通過類名和類加載器計算hash值,class_loader是Handle類型,而Handle._value的類型是oop*。而loader_data是ClassLoaderData*類型
  unsigned int d_hash = dic->compute_hash(name, loader_data);
  // 計算在hash中的索引位置
  int d_index = dic->hash_to_index(d_hash);
  // 根據hash和index 查到對應的klassOop
  Klass* probe = dic->find(d_index, d_hash, name, loader_data,protection_domain, THREAD);
  if (probe != NULL){
       return probe; // 如果直接找到的話,則返回
  }
  // ...  省略其它查找的邏輯
}

如果類還沒有加載,那么當前的方法還需要負責加載類。在實現的過程中考慮的因素比較多,比如解決並行加載、觸發父類的加載、域權限的驗證等,不過這些都不是我們要討論的重點,我們僅看加載的過程,此方法中有如下調用:

// Do actual loading
k = load_instance_class(name, class_loader, THREAD);

調用的方法如下:

// 體現出“雙親委派”機制,只要涉及到類的加載,都會調用這個函數
instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
  instanceKlassHandle nh = instanceKlassHandle(); // null Handle
  if (class_loader.is_null()) { // 使用引導類加載器來加載類

    // Search the shared system dictionary for classes preloaded into the shared spaces.
    // 在共享系統字典中搜索預加載到共享空間中的類,默認不使用共享空間,所以查找的結果為NULL
    instanceKlassHandle k;
    {
      k = load_shared_class(class_name, class_loader, THREAD);
    }

    if (k.is_null()) {
      // Use VM class loader,也就是系統類加載器進行類加載
      k = ClassLoader::load_classfile(class_name, CHECK_(nh));
    }
    // find_or_define_instance_class may return a different InstanceKlass
    // 調用SystemDictionary::find_or_define_instance_class->SystemDictionary::update_dictionary-> Dictionary::add_klass()將
    // 生成的Klass對象存起來。Dictionary是個hash表實現,使用的也是開鏈法解決hash沖突。
    if (!k.is_null()) {
      // 支持並行加載,也就是允許同一個類加載器同時加載多個類
      k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh)); 
    }
    return k;
  } else { // 使用指定的類加載器加載,最終會調用java.lang.ClassLoader類中的loadClass()方法執行類加載
    // Use user specified class loader to load class. Call loadClass operation on class_loader.
    ResourceMark rm(THREAD);

    JavaThread* jt = (JavaThread*) THREAD;

    Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
    // Translate to external class name format, i.e., convert '/' chars to '.'
    Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));

    JavaValue result(T_OBJECT);

    KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass());
    // 調用java.lang.ClassLoader類中的loadClass()方法進行類加載
    JavaCalls::call_virtual(&result,
                              class_loader,
                              spec_klass,
                              vmSymbols::loadClass_name(),
                              vmSymbols::string_class_signature(),
                              string,
                              CHECK_(nh));
//    assert(result.get_type() == T_OBJECT, "just checking");
    oop obj = (oop) result.get_jobject(); // 獲取調用loadClass()方法返回的Class對象

    // Primitive classes return null since forName() can not be
    // used to obtain any of the Class objects representing primitives or void
    if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
      // 獲取Class對象表示的Java類,也就是獲取表示Java類的instanceKlass對象
      instanceKlassHandle k = instanceKlassHandle(THREAD, java_lang_Class::as_Klass(obj));

      // For user defined Java class loaders, check that the name returned is
      // the same as that requested.  This check is done for the bootstrap
      // loader when parsing the class file.
      if (class_name == k->name()) {
        return k;
      }
    }
    // Class is not found or has the wrong name, return NULL
    return nh;
  }
}

當class_loader為NULL時,表示使用啟動類加載器加載類,調用ClassLoader::load_classfile()方法加載類;當class_loader不為NULL時,會調用java.lang.ClassLoader類中的loadClass()方法,相關方法在前面詳細介紹過,這里不再介紹。

使用引導類加載器加載類時,調用ClassLoader::load_classfile()方法加載類,這樣就得到了Klass對象,隨后調用的SystemDictionary::find_or_define_instance_class()方法只是會將這個Klass對象添加到字典中。函數的實現如下:

instanceKlassHandle SystemDictionary::find_or_define_instance_class(
	Symbol*               class_name,
	Handle                class_loader,
	instanceKlassHandle   k,
	TRAPS
) {

  instanceKlassHandle  nh = instanceKlassHandle(); // null Handle
  Symbol*              name_h = k->name(); // passed in class_name may be null
  ClassLoaderData*     loader_data = class_loader_data(class_loader);

  unsigned int         d_hash = dictionary()->compute_hash(name_h, loader_data);
  int                  d_index = dictionary()->hash_to_index(d_hash);

  // ...
  {
    MutexLocker mu(SystemDictionary_lock, THREAD);
    // First check if class already defined
    if (UnsyncloadClass || (is_parallelDefine(class_loader))) {
      Klass* check = find_class(d_index, d_hash, name_h, loader_data);
      if (check != NULL) {
        return(instanceKlassHandle(THREAD, check));
      }
    }

    // ...
  }
  
  define_instance_class(k, THREAD);

  // ...

  return k;
}

方法同樣會調用find_class()方法從字典中檢查一下這個類是否已經加載,如果加載了就直接返回,否則調用define_instance_class()函數。調用SystemDictionary::update_dictionary()函數將已經加載的類添加到系統詞典Map里面,如下:

// Update system dictionary - done after check_constraint and add_to_hierachy have been called.
源代碼位置:hotspot/src/share/vm/classfile/systemDictionary.cpp
void SystemDictionary::update_dictionary(
	int                  d_index,
	unsigned int        d_hash,
	int                  p_index,
	unsigned int        p_hash,
	instanceKlassHandle  k,
	Handle               class_loader,
	TRAPS
) {
  // Compile_lock prevents systemDictionary updates during compilations
  assert_locked_or_safepoint(Compile_lock);
  Symbol*  name  = k->name();
  ClassLoaderData *loader_data = class_loader_data(class_loader);

  {
	  MutexLocker mu1(SystemDictionary_lock, THREAD);

	  // Make a new system dictionary entry.
	  Klass* sd_check = find_class(d_index, d_hash, name, loader_data);
	  if (sd_check == NULL) {
		  dictionary()->add_klass(name, loader_data, k);
		  notice_modification();
	  }
          SystemDictionary_lock->notify_all();
  }
}

其中key使用類的包路徑+類名,類加載器兩者確定,value則為具體加載的類對應的instanceKlassHandle對象,其中維護這kclass對象。也就是系統詞典里面使用類加載器和類的包路徑類名唯一確定一個類。這也驗證了在Java中同一個類使用兩個類加載器進行加載后,加載的兩個類是不一樣的,是不能相互賦值的。 

3.findClass()方法

調用findClass()方法完成類的加載請求,這個方法會調用本地函數defineClass1(),實現如下: 

static native Class<?> defineClass1(ClassLoader loader, String name, byte[] b, int off, int len,
                                    ProtectionDomain pd, String source); 

definClass1()對應的JNI方法為 Java_java_lang_ClassLoader_defineClass1(),實現如下:

源代碼位置:/openjdk/jdk/src/share/native/java/lang/ClassLoader.c

JNIEXPORT jclass JNICALL Java_java_lang_ClassLoader_defineClass1(
  JNIEnv  *env,
  jclass    cls,
  jobject  loader,
  jstring   name,
  jbyteArray data,
  jint        offset,
  jint        length,
  jobject   pd,
  jstring   source
){
    // ...
    result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);
    // ...
    return result;
}

Java_java_lang_ClassLoader_defineClass1()函數主要是調用了JVM_DefineClassWithSource()函數加載類,最終調用的是jvm_define_class_common()函數。核心的實現邏輯如下: 

源代碼位置:hotspot/src/share/vm/prims/jvm.cpp

JVM_ENTRY(jclass, JVM_DefineClassWithSource(
		JNIEnv *env,
		const char *name,
		jobject loader,
		const jbyte *buf,
		jsize len,
		jobject pd,
		const char *source
))

  return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD);
JVM_END


static jclass jvm_define_class_common(
	JNIEnv        *env,
	const char   *name,
	jobject       loader,
	const jbyte  *buf,
	jsize         len,
	jobject       pd,
	const char   *source,
	jboolean      verify,
	TRAPS
) {
 
  JavaThread* jt = (JavaThread*) THREAD; 

  // Since exceptions can be thrown, class initialization can take place
  // if name is NULL no check for class name in .class stream has to be made.
  TempNewSymbol class_name = NULL;
  if (name != NULL) {
    const int str_len = (int)strlen(name);
    if (str_len > Symbol::max_length()) {
      // It's impossible to create this class;  the name cannot fit
      // into the constant pool.
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    }
    class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
  }

  ResourceMark rm(THREAD);
  /*
    利用 ClassFileStream 將要加載的class文件轉成文件流,然后調用SystemDictionary::resolve_from_stream(),生成Class在JVM中的代表:Klass
  */
  ClassFileStream st((u1*) buf, len, (char *)source);
  Handle class_loader (THREAD, JNIHandles::resolve(loader));
  Handle protection_domain (THREAD, JNIHandles::resolve(pd));
  Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader,protection_domain, &st,verify != 0,CHECK_NULL);

  return (jclass) JNIHandles::make_local(env, k->java_mirror());
}

上面這段邏輯主要就是利用 ClassFileStream 將要加載的Class文件轉成文件流,然后調用SystemDictionary::resolve_from_stream()函數生成 Class 在 HotSpot 中的表示Klass。resolve_from_stream()函數的實現如下:

// Add a klass to the system from a stream (called by jni_DefineClass and
// JVM_DefineClass).
// Note: class_name can be NULL. In that case we do not know the name of
// the class until we have parsed the stream.
Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
                                             Handle class_loader,
                                             Handle protection_domain,
                                             ClassFileStream* st,
                                             bool verify,
                                             TRAPS) {

  // ...
  // 解析文件流,生成 InstanceKlass
  ClassFileParser cfp = ClassFileParser(st);
  instanceKlassHandle k = cfp.parseClassFile(class_name,
											 loader_data,
											 protection_domain,
											 parsed_name,
											 verify,
											 THREAD);

  // ...
  if (!HAS_PENDING_EXCEPTION) {
    // Add class just loaded
    // If a class loader supports parallel classloading handle parallel define requests
    // find_or_define_instance_class may return a different InstanceKlass
    // 利用SystemDictionary注冊生成的 Klass
    // SystemDictionary 是用來幫助保存 ClassLoader 加載過的類信息的。准確點說,SystemDictionary
    // 並不是一個容器,真正用來保存類信息的容器是 Dictionary,每個ClassLoaderData 中都保存着一個私有的
    // Dictionary,而 SystemDictionary 只是一個擁有很多靜態方法的工具類而已。
    if (is_parallelCapable(class_loader)) {
      k = find_or_define_instance_class(class_name, class_loader, k, THREAD);
    } else {
      // 如果禁止了並行加載,那么直接利用SystemDictionary將 InstanceKlass
      // 注冊到 ClassLoader的 Dictionary 中即可
      define_instance_class(k, THREAD);
    }
  }

  return k();
}

調用parseClassFile()完成類的解析,然后調用find_or_define_instance_class()或define_instance_class()方法完成在SystemDictionary中的注冊。 

SystemDictionary 是用來幫助保存 ClassLoader 加載過的類信息的。准確點說,SystemDictionary 並不是一個容器,真正用來保存類信息的容器是 Dictionary,每個ClassLoaderData 中都保存着一個私有的 Dictionary,而 SystemDictionary 只是一個擁有很多靜態方法的工具類而已。

如上省略了一些邏輯,如支持並行加載等邏輯。如果允許並行加載,那么前面就不會對 ClassLoader 加鎖,所以在同一時刻,可能對同一 Class 文件加載了多次。

但是同一 Class 在同一 ClassLoader 中必須保持唯一性,所以這里會先利用 SystemDictionary 查詢 ClassLoader 是否已經加載過相同 Class。

如果已經加載過,那么就將當前線程剛剛加載的 InstanceKlass 加入待回收列表,並將 InstanceKlass* k 重新指向利用 SystemDictionary 查詢到的 InstanceKlass。如果沒有查詢到,那么就將剛剛加載的 InstanceKlass 注冊到 ClassLoader 的 Dictionary 中。 雖然並行加載不會鎖住 ClassLoader ,但是會在注冊 InstanceKlass 時對 SystemDictionary 加鎖,所以不需要擔心 InstanceKlass 在注冊時的並發操作。如果禁止了並行加載,那么直接利用 SystemDictionary 將 InstanceKlass 注冊到 ClassLoader 的 Dictionary 中即可。

參考:https://zhuanlan.zhihu.com/p/60328095

實例2

更改實例1中的UserClassLoader類的loadClass()方法的實現,如下:

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    if (name.startsWith("com.jvm")) {
        return findClass(name);
    }
    return super.loadClass(name, resolve);
}

這樣像Student這樣的在com.jvm包下的類就會由用戶自定義的類加載器UserClassLoader類來加載了。

更改實例1中TestClassLoader類的實現,使用Student類型來接收clazz.newInstance()獲取到的Student對象,如下:

Student obj = (Student)clazz.newInstance();

實例運行后,拋出的異常的簡要信息如下:

Exception in thread "main" java.lang.ClassCastException: com.jvm.Student cannot be cast to com.jvm.Student

因為實例化的Student對象所屬的InstanceKlass是由UserClassLoader加載生成的,而我們要強轉的類型Student對應的InstanceKlass是由系統默認的ClassLoader生成的,所以本質上它們就是兩個毫無關聯的InstanceKlass,當然不能強轉。

相關文章的鏈接如下:

1、在Ubuntu 16.04上編譯OpenJDK8的源代碼 

2、調試HotSpot源代碼

3、HotSpot項目結構 

4、HotSpot的啟動過程 

5、HotSpot二分模型(1)

6、HotSpot的類模型(2)  

7、HotSpot的類模型(3) 

8、HotSpot的類模型(4)

9、HotSpot的對象模型(5)  

10、HotSpot的對象模型(6) 

11、操作句柄Handle(7)

12、句柄Handle的釋放(8)

13、類加載器 

作者持續維護的個人博客classloading.com

關注公眾號,有HotSpot源碼剖析系列文章!

  

參考文章:

(1)類加載器的實現

(2)類的預加載 

(3)Java類的加載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM