首先来介绍一下类加载的时机,下面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()调用此方法时,会执行类的初始化操作,我们在后面专门会介绍类的初始化,这里暂不介绍。
相关文章的链接如下:
1、在Ubuntu 16.04上编译OpenJDK8的源代码
13、类加载器
14、类的双亲委派机制
15、核心类的预装载
16、Java主类的装载
作者持续维护的个人博客classloading.com。
关注公众号,有HotSpot源码剖析系列文章!