第3.5篇-触发类的装载
首先来介绍一下类加载的时机,下面5种情况会导致类初始化,所以必然在此之前对类进行加载,如下:
-
当虚拟机启动时加载主类,之前已经详细介绍过主类的加载过程;
-
使用java.lang.reflect包的方法对类进行反射调用的时候,如果类还没有初始化过,则需要进行初始化。
-
new一个类的对象,调用类的静态成员(除了由final修饰的常量外)和静态方法,无论是在解析执行还是编译执行的情况下,都会在处理new、getstatic、putstatic 或invokestatic字节码指令时对类进行初始化,在第9章会介绍使用new字节码指令创建对象的过程,其中就会有触发类加载的逻辑判断;
-
当初始化一个类,如果其父类没有被初始化,则先初始化其父类,后续在介绍函数InstanceKlass::initialize_impl()时会看到这个判断逻辑;
-
在使用JDK7的动态语言支持时,如果一个java.lang.invoke.MethodHandle对象最后的解析结果是REF_ getStatic、REF_ putStatic、REF_ invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。我们暂不讨论动态语言相关知识点,所以不在过多介绍。
可以通过调用ClassLoader类的loadClass()方法加载类,还可以通过调用java.lang.Class.forName()方法通过反射的方式完成加载类,但是loadClass()方法只是将Class文件加载到HotSpot VM中,而forName()方法会完成类的加载、链接和初始化过程。
forName()方法的实现如下:
源代码位置:openjdk/jdk/src/share/classes/java/lang/Class.java public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); // 第2个参数的值为true,表示要对类进行初始化 return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
调用的forName0()是一个本地静态方法,HotSpot VM提供了这个方法的本地接口实现,如下:
源代码位置: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()函数的实现如下:
源代码位置:openjdk/hotspot/src/share/vm/prims/jvm.cpp 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()函数的实现如下:
源代码位置:openjdk/hotspot/src/share/vm/prims/jvm.cpp jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) { // 遵循双亲委派机制加载类 Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL); KlassHandle klass_handle(THREAD, klass); 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()方法调用到此函数时,还会调用initialize()函数执行类的初始化操作,我们在后面会专门介绍类的初始化,这里暂不介绍。