ClassLoader 是 JVM 运行的重要组件,负责将字节码文件加载成 Class 对象。JVM 采用多层次的 ClassLoader 结构,不同层次的 ClassLoader 负责不同的类加载。在 JVM 中,Java 代码和 c++ 代码共同保证 ClassLoader 的完整性。本文将从 jvm 源码层面阐述 ClassLoader 的功能,展现出完整的 ClassLoader 架构图。

内置 ClassLoader

BuiltinClassLoader 翻译成中文叫做内置 ClassLoader。它有三个子类,分别是 BootClassLoader、PlatformClassLoader、AppClassLoader三个子类,分别负责不同路径下类的加载。

BuiltinClassLoader 三个子类形成逻辑上的父子关系。

public class BuiltinClassLoader{
    // parent ClassLoader
    private final BuiltinClassLoader parent;
    // the URL class path, or null if there is no class path
    private @Stable URLClassPath ucp;
}
// BootClassLoader
//     ↑ parent 
// PlatformClassLoader
//     ↑ parent 
// AppClassLoader
  • BootClassLoader: 加载 Java 核心类例如 java.lang.Object 和标准库中的类。
  • Platform Class Loader: 替代了 Extension Class Loader,Platform Class Loader 主要负责加载 Java 平台模块(如 java.sql, java.xml 等)中定义的类。
  • AppClassLoader: 主要负责加载 CLASSPATH 或通过 -classpath 或 -cp 参数指定的路径中的类和资源,它是大多数应用程序开发者直接或间接使用的类加载器。

ClassLoader 初始化

BootClassLoader 不仅在 Java 层面有对应实例,而且 JVM 初始化时在 C++ 层面也有对应的实例。

C++ 层面初始化请看前文 BootClassLoader 初始化

ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = nullptr;

void ClassLoaderData::init_null_class_loader_data() {
    //Handle()  { _handle = nullptr; }
  _the_null_class_loader_data = new ClassLoaderData(Handle(), false);
}

Java 层面的初始化由静态代码块定义,即当 ClassLoaders 被加载的时候会初始化。

public class ClassLoaders {
  static{
    BOOT_LOADER = new BootClassLoader(bootUcp);
    PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
    APP_LOADER = new AppClassLoader(PLATFORM_LOADER, ucp);
  }
}

JVM 初始化的时候会加载一批类,见前文 类加载

部分定义代码

  do_klass(jdk_internal_loader_ClassLoaders_klass,      jdk_internal_loader_ClassLoaders                      ) \

  do_klass(jdk_internal_loader_ClassLoaders_AppClassLoader_klass,      jdk_internal_loader_ClassLoaders_AppClassLoader) \

  do_klass(jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass, jdk_internal_loader_ClassLoaders_PlatformClassLoader) \

为什么没有加载 BootClassLoader ?

上述 ClassLoader 都是被 _the_null_class_loader_data 这个 C++ ClassLoader 加载的。

上述代码执行完成以后,BootClassLoader、PlatformClassLoader、AppClassLoader 这几个对象也就被创建出来了。

// BootClassLoader
//     ↑ parent 
// PlatformClassLoader
//     ↑ parent 
// AppClassLoader

BootClassLoader / _the_null_class_loader_data 加载类

_the_null_class_loader_data 是对最顶层加载器的封装,此时的加载器就是 nullptr。

_the_null_class_loader_data = new ClassLoaderData(Handle(), false);

Handle(){ _handle = nullptr; }

BootClassLoader 加载类时最终也是调用_the_null_class_loader_data 的逻辑。

下面是 BootClassLoader 的部分逻辑。

private static class BootClassLoader extends BuiltinClassLoader {
    protected Class<?> loadClassOrNull(String cn, boolean resolve) {
        return JLA.findBootstrapClassOrNull(cn);
    }
};
//-> findBootstrapClassOrNull 
//-> ClassLoader.findBootstrapClassOrNull
private static native Class<?> findBootstrapClass(String name);

JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_findBootstrapClass(JNIEnv *env, jclass dummy,
                                              jstring classname){
  cls = JVM_FindClassFromBootLoader(env, clname);
}

loadClassOrNull方法最终调用 JVM_FindClassFromBootLoader 函数。

JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                              const char* name))
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
return (jclass) JNIHandles::make_local(THREAD, k->java_mirror());
JVM_END

//-> resolve_or_null -> resolve_or_null
resolve_or_null(class_name, Handle(), THREAD); //Handle(){ _handle = nullptr; }

//->resolve_instance_class_or_null
InstanceKlass* SystemDictionary::
  resolve_instance_class_or_null(Symbol* name, Handle class_loader, TRAPS) {
  ClassLoaderData* loader_data = register_loader(class_loader);

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

//->register_loader
return (class_loader() == nullptr) ? ClassLoaderData::the_null_class_loader_data() :
                                      ClassLoaderDataGraph::find_or_create(class_loader);

函数 register_loader 根据判断是否使用 the_null_class_loader_data。由于使用 Handle() 所以这里是 ClassLoaderData::the_null_class_loader_data()

//load_instance_class -> load_instance_class_impl
InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) {
  if (class_loader.is_null()) { // BootClassLoader
    if (k == nullptr) { // Use VM class loader
      k = ClassLoader::load_class(class_name, pkg_entry, search_only_bootloader_append, CHECK_NULL);
    }

    // find_or_define_instance_class may return a different InstanceKlass
    if (k != nullptr) {
      // If a class loader supports parallel classloading handle parallel define requests.
      // find_or_define_instance_class may return a different InstanceKlas
      k = find_or_define_instance_class(class_name, class_loader, k, CHECK_NULL);
    }
    return k
  }else{
    // other ClassLoader
  }
}

InstanceKlass* ClassLoader::load_class(Symbol* name, PackageEntry* pkg_entry, bool search_append_only, TRAPS) {
  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
  InstanceKlass* result = KlassFactory::create_from_stream(stream, name,
                                                           loader_data,
                                                           cl_info, CHECK_NULL)
}

最终在函数 load_class 中调用 KlassFactory::create_from_stream 实现类加载。见前文 类解析

其他类加载器

在前文 加载主类 中有代码 Class.forName(cn, false, scl) 加载主类,最终调用 native 方法。

//loader = ClassLoader.getSystemClassLoader();
private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader, Class<?> caller);
//-> Java_java_lang_Class_forName0
//-> JVM_FindClassFromCaller
//-> find_class_from_class_loader
// Find a class with this name in this loader, using the caller's protection domain.
JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,
                                          jboolean init, jobject loader,
                                          jclass caller))
return find_class_from_class_loader(env, h_name, init, h_loader, false, THREAD)
JVM_END

函数 find_class_from_class_loader 最终会调用 load_instance_class_impl

InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) {
  // Use user specified class loader to load class. Call loadClass operation on class_loader.
   if (class_loader.is_null()) { /* BootClassLoader */}
   else{// 其他类加载器
    JavaCalls::call_virtual(&result,
                        class_loader,
                        spec_klass,
                        vmSymbols::loadClass_name(),
                        vmSymbols::string_class_signature(),
                        string,
                        CHECK_NULL);
    return InstanceKlass::cast(java_lang_Class::as_Klass(obj));
   }
}

上面代码 JavaCalls::call_virtual 调用的是 vmSymbols::loadClass_name() 方法。

template(loadClass_name, "loadClass");

对应于 Java 方法 ClassLoader#loadClass(String name),此代码就是通常说的双亲委派模型的实现。

其中 parent.loadClass 继续调用 loadClass 方法。

findBootstrapClassOrNull 前文已经论述过。

public Class<?> loadClass(String name) throws ClassNotFoundException {
  return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve) {
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name); // 查询是否已经在被加载过
        if (c == null) {
            if (parent != null) {
                c = parent.loadClass(name, false); //使用父类加载器加载
            } else {
                c = findBootstrapClassOrNull(name); //在顶层加载器中搜索
            }
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClass(name); //当前加载器加载
            }
        }
        return c;
    }
}

但是对于内置的 ClassLoader 都是继承自 BuiltinClassLoader,重写了 loadClass 方法。而BootClassLoaderloadClassOrNull 进行了重写。

所以本文这里JavaCalls::call_virtual 调用的是 BuiltinClassLoader#loadClass,增了模块化的支持。其他逻辑与 ClassLoader#loadClass 类似。

//BuiltinClassLoader
@Override
protected Class<?> loadClass(String cn, boolean resolve) {
    return loadClassOrNull(cn, resolve);
}

private static class BootClassLoader extends BuiltinClassLoader {
    @Override
    protected Class<?> loadClassOrNull(String cn, boolean resolve) {
        return JLA.findBootstrapClassOrNull(cn);
    }
};

findLoadedClass

回到 ClassLoader#loadClass ,看看 findLoadedClass 方法是如何实现的。

private final native Class<?> findLoadedClass0(String name);

//-> Java_java_lang_ClassLoader_findLoadedClass0
//-> JVM_FindLoadedClass
JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
  SystemDictionary::find_instance_or_array_klass(THREAD, klass_name, h_loader);
JVM_EN

//find_instance_or_array_klass -> find_instance_klass
InstanceKlass* SystemDictionary::find_instance_klass(Thread* current,
                                                     Symbol* class_name,
                                                     Handle class_loader) {
Dictionary* dictionary = loader_data->dictionary();
return dictionary->find_class(current, class_name)
}

BuiltinClassLoader#findClassOnClassPathOrNull

findClassOnClassPathOrNull 最终会调用 native 方法 defineClass1

private Class<?> findClassOnClassPathOrNull(String cn) {
   return defineClass(cn, res);
}

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

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){
  body = (jbyte *)malloc(length);
  (*env)->GetByteArrayRegion(env, data, offset, length, body);
  return JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);
}

GetByteArrayRegion 方法会将字节码内容拷贝到新分配的数组中。 GetByteArrayRegion 也用于解决 G1 中 region pin 的问题。见 region pinning for g1

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(name, loader, buf, len, pd, source, THREAD);
JVM_END

//->jvm_define_class_common
//->SystemDictionary::resolve_from_stream

最终调用 resolve_from_stream 解析类。

总结

本文从 Java 和 C++ 源码介绍了内置三个 ClassLoader 的创建,以及他们是如何加载 Java 类的。希望本文对于想深入理解 ClassLoader 机制的读者有所作用。