原创

触发类的装载

首先来介绍一下类加载的时机,下面5种情况会导致类初始化,所以必然在此之前对类进行加载,如下:(参考:深入理解Java虚拟机)

  • 当虚拟机启动时加载主类,之前对于主类的加载时详细介绍过;
  • 使用java.lang.reflect包的方法对类进行反射调用;
  • new一个类的对象,调用类的静态成员(除了由final修饰的常量外)和静态方法,无论是在解析执行还是编译执行情况下,都会在处理new、getstatic、putstatic 或invokestatic字节码指令时需要对类进行初始化;
  • 当初始化一个类,如果其父类没有被初始化,则先初始化它父类,后续在介绍初始化方法InstanceKlass::initialize_impl()时会看到这个逻辑;
  • 当使用JDK 1. 7 的动态语言支持时,如果一个java. lang. invoke. MethodHandle 实例最后的解析结果REF getStatic、REF putStatic、REF_ invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
  • 关于主类的加载在之前已经介绍过,通过调用ClassLoader类的loadClass()方法来完成,还可以通过调用java.lang.Class.forName()方法通过反射的方法完成类加载,但是loadClass()只是将Class文件加载到HotSpot中,而forName()方法会完成方法的加载、链接和初始化。

forName()方法的实现如下:

@CallerSensitive
public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        // 第2个参数的值为true,表示要对类进行初始化 
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

调用的forName0()是一个本地静态方法,如下:

private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

HotSpot提供了这个方法的本地接口实现,如下:

源代码位置:openjdk/jdk/src/share/native/java/lang/Class.c

JNIEXPORT jclass JNICALL
Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,
                              jboolean initialize, jobject loader)
{
    char *clname;
    jclass cls = 0;

    cls = JVM_FindClassFromClassLoader(env, clname, initialize,
                                       loader, JNI_FALSE);

    return cls;
}

调用JVM_FindClassFromClassLoader()函数,实现如下:


JVM_ENTRY(jclass, JVM_FindClassFromClassLoader(JNIEnv* env, const char* name,
                                               jboolean init, jobject loader,
                                               jboolean throwError))
  // ...
  TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
  Handle h_loader(THREAD, JNIHandles::resolve(loader));
  jclass result = find_class_from_class_loader(env, h_name, init, h_loader,
                                               Handle(), throwError, THREAD);
  return result;
JVM_END

调用的find_class_from_class_loader()函数的实现如下:

jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) {
  // Security Note:
  //   The Java level wrapper will perform the necessary security check allowing
  //   us to pass the NULL as the initiating class loader.
  Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL);

  KlassHandle klass_handle(THREAD, klass);
  // Check if we should initialize the class
  if (init && klass_handle->oop_is_instance()) { // init的值为true
    klass_handle->initialize(CHECK_NULL); // 对类进行初始化操作
  }
  return (jclass) JNIHandles::make_local(env, klass_handle->java_mirror());
}

调用SystemDictionary::resolve_or_fail()在之前介绍过,方法会遵循双亲委派机制来加载类,通常是创建或从Dictionary中查询已经加载的instanceKlass实例,不涉及到对类的连接、初始化等。通过forName()调用此方法时,会执行类的初始化操作,我们在后面专门会介绍类的初始化,这里暂不介绍。

正文到此结束