类的加载

2016/01/23 JVM

类加载器

类加载器可以加载类,这些类被HotSpot加载后,都以Klass对象表示。涉及到的主要的类加载器有启动类加载器/引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用类加载器/系统类加载器(Application ClassLoader)。

引导类加载器/启动类加载器

引导类加载器由ClassLoader类实现,这个ClassLoader类是用C++语言实现的,负责将 /lib目录、 -Xbootclasspath选项指定的目录或系统属性sun.boot.class.path指定的目录下的核心类库加载到内存中。

用C++语言定义的类加载器及重要的函数如下:

源代码位置:hotspot/src/share/vm/classfile/classLoader.hpp

class ClassLoader::AllStatic {
private:
   // First entry in linked list of ClassPathEntry instances
   // ClassPathEntry类指针,ClassPathEntry用于表示单个classpath路径,
   // 所有的ClassPathEntry实例以链表的形式关联起来,_first_entry表示链表的第一个实例
   static ClassPathEntry* _first_entry;
   // Last entry in linked list of ClassPathEntry instances
   // 表示链表的最后一个实例
   static ClassPathEntry* _last_entry;
   // Hash table used to keep track of loaded packages
   // 用于保存已经加载过的包名
   static PackageHashtable* _package_hash_table;
 
   // ...
   // 加载类
   static instanceKlassHandle load_classfile(Symbol* h_name,TRAPS);
   // 设置加载路径
   static void setup_bootstrap_search_path();
public:
   // 初始化类加载器
   static void initialize();
   // ...
}

通过_first_entry链表保存这个类加载器可以加载的一些类路径。在虚拟机启动时会通过调用ClassLoader::setup_bootstrap_search_path()函数来设置。

load_classfile()方法可以根据类名加载类,具体实现如下:

源代码位置:openjdk/hotspot/src/share/vm/classfile/classLoader.cpp

instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
  // 获取类名
  const char* class_name = h_name->as_C_string();
  ....
  
  stringStream st;
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  // 获取文件名
  const char* file_name = st.as_string();
  ClassLoaderExt::Context context(class_name, file_name, THREAD);
  
  // ClassFileStream表示Class文件的字节流
  ClassFileStream* stream = NULL;
  int classpath_index = 0;
  ClassPathEntry* e = NULL;
  instanceKlassHandle h;
  {
    //从第一个ClassPathEntry开始遍历所有的ClassPathEntry
    e = _first_entry;
    while (e != NULL) {
      stream = e->open_stream(file_name, CHECK_NULL);
      // 如果检查返回false则返回null,check方法默认返回true
      if (!context.check(stream, classpath_index)) {
        return h; // NULL
      }
      // 如果找到目标文件则跳出循环
      if (stream != NULL) {
        break;
      }
      e = e->next();
      ++classpath_index;
    }
  }
  //如果找到了目标Class文件
  if (stream != NULL) {
    // 构建一个ClassFileParser实例
    ClassFileParser parser(stream);
    // 构建一个ClassLoaderData实例
    ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
    Handle protection_domain;
    TempNewSymbol parsed_name = NULL;
    // 解析并加载class文件,注意此时并未开始链接
    instanceKlassHandle  result = parser.parseClassFile(h_name,loader_data,protection_domain,parsed_name,false,CHECK_(h));
    ...
    // 调用ClassLoader的add_package方法,把当前类的包名加入到_package_hash_table中
    if (add_package(name, classpath_index, THREAD)) {
       h = result;
    }
  } 
 
  return h;
}

每个类加载器都对应着一个ClassLoaderData对象,通过ClassLoaderData::the_null_class_loader_data()函数获取引导类加载器对应的ClassLoaderData对象。

调用add_package()将已经解析过的类进行保存,避免重复加载解析。逻辑实现并不复杂,这里不在介绍。

parseClassFile()方法就是解析Class文件中的类、字段、常量池等信息,然后转换为C++内部的对等表示,如类元信息存储在InstanceKlass实例中,常量池信息存储在ConstantPool中,部分的C++对等实现(类模型)在之前已经介绍过,这里不再介绍。后续会详细介绍parseClassFile()方法解析Class文件的过程。

扩展类加载器

扩展类加载器用Java语言编写,由sun.misc.Launcher$ExtClassLoader类实现,负责将 /lib/ext目录或者由系统变量-Djava.ext.dir所指定的目录中的类库加载到内存中。

扩展类加载器ExtClassLoader的实现如下:

源代码位置:openjdk/jdk/src/share/classes/sun/misc/Launcher.java

static class ExtClassLoader extends URLClassLoader {
  
    /**
     * create an ExtClassLoader. The ExtClassLoader is created
     * within a context that limits which files it can read
     */
    public static ExtClassLoader getExtClassLoader() throws IOException
    {
        final File[] dirs = getExtDirs(); // 获取要加载类的加载路径
  
        ...
        return new ExtClassLoader(dirs); // 实例化扩展类加载器
        ...
    }
  
    /*
     * Creates a new ExtClassLoader for the specified directories.
     */
    public ExtClassLoader(File[] dirs) throws IOException {
        super(getExtURLs(dirs), null, factory); // parent传递的参数为null,所以并不是引导类加载器
    }
  
    private static File[] getExtDirs() {
        String s = System.getProperty("java.ext.dirs");
        File[] dirs;
        if (s != null) {
            StringTokenizer st = new StringTokenizer(s, File.pathSeparator);
            int count = st.countTokens();
            dirs = new File[count];
            for (int i = 0; i < count; i++) {
                dirs[i] = new File(st.nextToken());
            }
        } else {
            dirs = new File[0];
        }
        return dirs;
    } 
    ...
}

ExtClassLoader类的构造函数中在调用父类的构造函数时,传递的第2个参数的值为null,这个值最终会赋值给parent字段,所以后面将会讲到,当这个字段的值为null时,java.lang.ClassLoader类中实现的loadClass()方法在加载一个类时将会调用findBootstrapClassOrNull()方法,而这个方法最终会调用C++实现的ClassLoader类的相关方法进行类加载。

系统类加载器/应用类加载器

系统类加载器用Java语言编写,由sun.misc.Launcher$AppClassLoader类实现,负责将系统环境变量-classpath、-cp或系统属性java.class.path指定的路径下的类库加载到内存中。

扩展类加载器AppClassLoader的实现如下:

源代码位置:openjdk/jdk/src/share/classes/sun/misc/Launcher.java

        public static ClassLoader getAppClassLoader(final ClassLoader extcl)throws IOException {
            final String s = System.getProperty("java.class.path");
            final File[] path = (s == null) ? new File[0] : getClassPath(s);
  
            // ...
            return new AppClassLoader(urls, extcl);
        }
  
        /*
         * Creates a new AppClassLoader
         */
        AppClassLoader(URL[] urls, ClassLoader parent) {
            super(urls, parent, factory); // parent是一个扩展类加载器实例
        }
  
        /**
         * Override loadClass so we can checkPackageAccess.
         */
        public Class loadClass(String name, boolean resolve)throws ClassNotFoundException{
            // ...
            return (super.loadClass(name, resolve));
        }
         
        // ...
}

在Launcher类的构造函数中实例化系统类加载器AppClassLoader时,会调用getAppClassLoader()方法获取系统类加载器,传入的参数是一个扩展类加载器ExtClassLoader实例,这样系统类加载器的父加载器就变成了扩展类加载器(与父加载器并非继承关系)。用户自定义的无参类加载器的父类加载器默认就是AppClassloader类加载器。

构造类加载器实例

HotSpot在启动过程中会在/lib/rt.jar包里面的sun.misc.Launcher类中完成扩展类加载器和系统类加载器的实例化,也会进行引导类加载器的初始化,也就是调用C++语言编写的ClassLoader类的initialize()方法。

HotSpot在初始化时,会初始化一个重要的变量,定义如下:

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

oop SystemDictionary::_java_system_loader = NULL;

这个属性保存系统类加载器实例,HotSpot在加载主类时会使用这个类加载器加载主类。属性在compute_java_system_loader()方法中初始化,调用链路如下:

JavaMain()                                      java.c  
InitializeJVM()                                 java.c
JNI_CreateJavaVM()                              jni.cpp 
Threads::create_vm()                            thread.cpp
SystemDictionary::compute_java_system_loader()  systemDictionary.cpp

方法的实现如下:


void SystemDictionary::compute_java_system_loader(TRAPS) {
  KlassHandle  system_klass(THREAD, WK_KLASS(ClassLoader_klass));
  JavaValue    result(T_OBJECT);
  // 调用java.lang.ClassLoader类的getSystemClassLoader()方法
  JavaCalls::call_static(&result, // 调用Java静态方法的返回值存储在result中
                         KlassHandle(THREAD, WK_KLASS(ClassLoader_klass)), // 调用的目标类为java.lang.ClassLoader
                         vmSymbols::getSystemClassLoader_name(), // 调用目标类中的目标方法为getSystemClassLoader
                         vmSymbols::void_classloader_signature(), // 调用目标方法的方法签名
                         CHECK);
  // 获取调用getSystemClassLoader()方法的结果并保存到_java_system_loader属性中
  _java_system_loader = (oop)result.get_jobject();  // 初始化属性为系统类加载器/应用类加载器/AppClassLoader
}

通过JavaClass::call_static()方法调用java.lang.ClassLoader类的getSystemClassLoader()方法。JavaClass::call_static()方法非常重要,它是HotSpot调用Java静态方法的API,后面会详细介绍。

下面看一下getSystemClassLoader()方法的实现,如下:

源代码位置:openjdk/jdk/src/share/classes/java/lang/ClassLoader.java 

private static ClassLoader scl;<br><br>public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        return scl;
}

private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); // 获取Launcher实例
            if (l != null) {
                scl = l.getClassLoader(); // 获取类加载器实例
                // ...               
            }
            sclSet = true;
        }
}

如上方法及变量定义在java.lang.ClassLoader类中。

在getSystemClassLoader()函数中调用Launcerh.getLauncher()方法获取Launcher实例,实例通过静态变量launcher来保存,静态变量的定义如下:

源代码位置:openjdk/jdk/src/share/classes/sum/misc/Launcher.java

private  static   Launcher launcher = new  Launcher();

调用l.getClassLoader()方法获取类加载器实例,如下:

源代码位置:openjdk/jdk/src/share/classes/sum/misc/Launcher.java

public ClassLoader getClassLoader() {
     return loader; // 返回的loader就是Launcher类的loader,也就是应用类加载器AppClassLoader
}

Launcher()类的构造函数如下:

源代码位置:openjdk/jdk/src/share/classes/sun/misc/Launcher.java

private ClassLoader loader;
 
public Launcher() {
        // Create the extension class loader
        ClassLoader extcl;
        try { 
            // 首先创建了扩展类加载器
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError("Could not create extension class loader", e);
        }
  
        // Now create the class loader to use to launch the application
        try { 
            // 以ExtClassloader作为父加载器创建了AppClassLoader
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError("Could not create application class loader", e);
        }
  
        // Also set the context class loader for the primordial thread. 
        // 默认线程上下文加载器为AppClassloader
        Thread.currentThread().setContextClassLoader(loader); 
}

如上方法及变量定义在sumn.misc.Lanucher类中。

可以看到有对ExtClassLoader与AppClassLoader实例创建的逻辑,这样HotSpot就可以通过_java_system_loader属性获取AppClassLoader实例,通过AppClassLoader实例中的parent属性使用ExtClassLoader。 

类的双亲委派机制

双亲委派模型中的各个类加载器之间并不表示继承关系,而是表示工作过程,具体说就是,对于一个加载类的具体请求,首先要委派给自己的父类去加载,只有当父类无法完成加载请求时,子类自己才会去尝试加载。类加载器加载类时,不会涉及到类的连接、初始化等步骤,仅将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()方法。

下面详细介绍loadClass()方法中调用的findLoaderClass()、findBootstrapClassOrNull()与findClass()方法的实现。

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。

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中同一个类使用两个类加载器进行加载后,加载的两个类是不一样的,是不能相互赋值的。

Search

    微信好友

    博士的沙漏

    Table of Contents