这是我人生中第一次写技术博客,我也看过很多优秀博主博客从中学习到了很多;介于我也是一个人热爱技术分享的人,所以我想把自己在工作学习中获得知识分享给热爱技术的各位同胞还可以巩固知识,何乐而不为呢!
了解类加载器之前需要有一定c/c++、Java基础以及源码阅读能力,本次分享源码篇幅较多,有那里写的不好地方或者错误欢迎各位留言指正问题,废话不多说进入正题!
在jvm中加载编译好的二进制文件.claas是通过各种类加载器进行装载,java之所以跨平台、动态加载特性都源于此,装载后又是如果管理某个类的生命周期呢?有以下七个阶段如下图:其中验证、准备、解析统称为连接
以上每个阶段作用就不一一解释了,下面我们看下class.forName源码它是如何来加载一个类
@CallerSensitive public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }复制代码
我们重点关注forName0方法
/** Called after security check for system loader access checks have been made. */ private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException;复制代码
从上述源码看来代码并不多,但是我看到了native关键字,这代表着该方法为本地方法JNI(动态链接库)也就是Java调用的c或c++写的函数那她又是如何调用呢?又是如果做加载工作呢?这里我们先卡顿下,我们先来看看方法里的具体的四个参数
name:其实className,需要对某一个类做加载操作对类名;
initialize:是否初始化,初始化是指该类里的静态代码static,默认为true
loader:类加载器,BootstrapClassLoader、ExtClassLoader、AppClassLoader
caller:指定使用类加载器
这是我从百度百科获取简单JNI实现原理,大家应该都能看懂,具体细节需要大家自行了解!
JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 [1] 从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
JDK源码下载
hg.openjdk.java.net/jdk/jdk/
JNI相关api可以参考官方文档docs.oracle.com/javase/8/do…
通过上述我们了解到Java通过JNI方式调用底层c或c++实现的函数库,那么我就开始看看这些实现JNI具体干了些啥
//源码路径:src/java.base/share/native/libjava/Class.c JNIEXPORT jclass JNICALL Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname, jboolean initialize, jobject loader, jclass caller) { char *clname; jclass cls = 0; char buf[128]; jsize len; jsize unicode_len; if (classname == NULL) { JNU_ThrowNullPointerException(env, 0); return 0; } len = (*env)->GetStringUTFLength(env, classname); unicode_len = (*env)->GetStringLength(env, classname); if (len >= (jsize)sizeof(buf)) { clname = malloc(len + 1); if (clname == NULL) { JNU_ThrowOutOfMemoryError(env, NULL); return NULL; } } else { clname = buf; } (*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname); //把类全限定名里的'.'翻译成'/' if (VerifyFixClassname(clname) == JNI_TRUE) { /* slashes present in clname, use name b4 translation for exception */ (*env)->GetStringUTFRegion(env, classname, 0, unicode_len, clname); JNU_ThrowClassNotFoundException(env, clname); goto done; } //验证类全限定名名合法性(是否以'/'分隔) if (!VerifyClassname(clname, JNI_TRUE)) { /* expects slashed name */ JNU_ThrowClassNotFoundException(env, clname); goto done; } //重点关注方法,从指定的加载器查找该类 cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller); done: if (clname != buf) { free(clname); } return cls; }复制代码
// 从指定的加载器查找该类JVM_FindClassFromCaller方法位于src/hotspot/share/prims/jvm.cpp// 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)) // 把当前类加入符号表(一个哈希表实现) TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL); // 获取加载器和调用类 oop loader_oop = JNIHandles::resolve(loader); oop from_class = JNIHandles::resolve(caller); oop protection_domain = NULL; if (from_class != NULL && loader_oop != NULL) { protection_domain = java_lang_Class::as_Klass(from_class)->protection_domain(); } // 查找该类 jclass result = find_class_from_class_loader(env, h_name, init, h_loader, h_prot, false, THREAD); // 返回结果 return result; JVM_END复制代码
加入符号表后紧接着在指定的classloader中查找该类,/src/hotspot/share/prims/jvm.cpp
// Shared JNI/JVM entry points ////////////////////////////////////////////////////////////// // 从指定的classloader中查找类 jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, jboolean throwError, TRAPS) { //========================================== // // 根据指定的类名和加载器返回一个Klass对象,必要情况下需要加载该类。 // 如果未找到该类则抛出NoClassDefFoundError或ClassNotFoundException // //========================================= Klass* klass = SystemDictionary::resolve_or_fail(name, loader, protection_domain, throwError != 0, CHECK_NULL); // Check if we should initialize the class if (init && klass->is_instance_klass()) { klass->initialize(CHECK_NULL); } return (jclass) JNIHandles::make_local(env, klass->java_mirror()); }复制代码
方法SystemDictionary::resolve_or_fail()
位于src/hotspot/share/classfile/systemDictionary.cpp
// Forwards to resolve_or_null Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) { Klass* klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD); if (HAS_PENDING_EXCEPTION || klass == NULL) { // can return a null klass klass = handle_resolution_exception(class_name, throw_error, klass, THREAD); } return klass; }复制代码
systemDictionary.cpp # resolve_or_null()
// Forwards to resolve_instance_class_or_null 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, THREAD); } else if (FieldType::is_obj(class_name)) { ResourceMark rm(THREAD); // Ignore wrapping L and ;. 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, THREAD); } else { // 解析实例类 return resolve_instance_class_or_null(class_name, class_loader, protection_domain, THREAD); } }复制代码
systemDictionary.cpp # resolve_instance_class_or_null()
Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle class_loader, Handle protection_domain, TRAPS) { Handle lockObject = compute_loader_lock_object(class_loader, THREAD); check_loader_lock_contention(lockObject, THREAD); // 获取对象锁 ObjectLocker ol(lockObject, THREAD, DoObjectLock); { MutexLocker mu(SystemDictionary_lock, THREAD); // 查找类 InstanceKlass* check = find_class(d_index, d_hash, name, dictionary); if (check != NULL) { // Klass is already loaded, so just return it class_has_been_loaded = true; k = check; } else { // 查找该类是否在placeholder table中 placeholder = placeholders()->get_entry(p_index, p_hash, name, loader_data); if (placeholder && placeholder->super_load_in_progress()) { super_load_in_progress = true; if (placeholder->havesupername() == true) { superclassname = placeholder->supername(); havesupername = true; } } } } // 如果该类在placeholder table中,则说明类加载进行中 if (super_load_in_progress && havesupername==true) { k = handle_parallel_super_load(name, superclassname, class_loader, protection_domain, lockObject, THREAD); if (HAS_PENDING_EXCEPTION) { return NULL; } if (k != NULL) { class_has_been_loaded = true; } } bool throw_circularity_error = false; if (!class_has_been_loaded) { bool load_instance_added = false; if (!class_has_been_loaded) { // ===================================== // // 执行实例加载动作 // // ===================================== k = load_instance_class(name, class_loader, THREAD); if (!HAS_PENDING_EXCEPTION && k != NULL && k->class_loader() != class_loader()) { check_constraints(d_index, d_hash, k, class_loader, false, THREAD); // Need to check for a PENDING_EXCEPTION again; check_constraints // can throw and doesn't use the CHECK macro. if (!HAS_PENDING_EXCEPTION) { { // Grabbing the Compile_lock prevents systemDictionary updates // during compilations. MutexLocker mu(Compile_lock, THREAD); update_dictionary(d_index, d_hash, p_index, p_hash, k, class_loader, THREAD); } // 通知JVMTI类加载事件 if (JvmtiExport::should_post_class_load()) { Thread *thread = THREAD; assert(thread->is_Java_thread(), "thread->is_Java_thread()"); JvmtiExport::post_class_load((JavaThread *) thread, k); } } } } // load_instance_class } ... return k; }复制代码
systemDictionary.cpp # load_instance_class()
// =================================================================================== // // 加载实例class,这里有两种方式: // =================================================================================== // // 1、如果classloader为null则说明是加载系统类,使用bootstrap loader // 调用方式:直接调用ClassLoader::load_class()加载该类 // // 2、如果classloader不为null则说明是非系统类,使用ext/app/自定义 classloader // 调用方式:通过JavaCalls::call_virtual()调用Java方法ClassLoader.loadClass()加载该类 // // =================================================================================== InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) { // 使用bootstrap加载器加载 if (class_loader.is_null()) { // 根据全限定名获取包名 // Find the package in the boot loader's package entry table. TempNewSymbol pkg_name = InstanceKlass::package_from_name(class_name, CHECK_NULL); if (pkg_name != NULL) { pkg_entry = loader_data->packages()->lookup_only(pkg_name); } InstanceKlass* k = NULL; if (k == NULL) { // Use VM class loader PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time()); // ================================================================= // // 使用bootstrap loader加载该类 // // ================================================================= k = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_NULL); } return k; } else { // ======================================================================================= // // 使用用户指定的加载器加载该类,调用class_loader的loadClass操作方法, // 最终返回一个标准的InstanceKlass,流程如下 // // +-----------+ loadClass() +---------------+ get_jobject() +-------------+ // | className | -------------> | JavaValue | ---------------> | oop | // +-----------+ +---------------+ +-------------+ // | // | as_Klass() // v // +---------------+ cast() +-------------+ // | InstanceKlass | <--------------- | Klass | // +---------------+ +-------------+ // // ======================================================================================= ResourceMark rm(THREAD); assert(THREAD->is_Java_thread(), "must be a JavaThread"); JavaThread* jt = (JavaThread*) THREAD; PerfClassTraceTime vmtimer(ClassLoader::perf_app_classload_time(), ClassLoader::perf_app_classload_selftime(), ClassLoader::perf_app_classload_count(), jt->get_thread_stat()->perf_recursion_counts_addr(), jt->get_thread_stat()->perf_timers_addr(), PerfClassTraceTime::CLASS_LOAD); Handle s = java_lang_String::create_from_symbol(class_name, CHECK_NULL); // Translate to external class name format, i.e., convert '/' chars to '.' Handle string = java_lang_String::externalize_classname(s, CHECK_NULL); JavaValue result(T_OBJECT); InstanceKlass* spec_klass = SystemDictionary::ClassLoader_klass(); // Added MustCallLoadClassInternal in case we discover in the field // a customer that counts on this call if (MustCallLoadClassInternal && has_loadClassInternal()) { JavaCalls::call_special(&result, class_loader, spec_klass, vmSymbols::loadClassInternal_name(), vmSymbols::string_class_signature(), string, CHECK_NULL); } else { // =============================================================== // // 调用ClassLoader.loadClass()方法加载该类,而最终会调用ClassLoader的native方法defineClass1() // 其实现位于ClassLoader.c # Java_java_lang_ClassLoader_defineClass1() // // =============================================================== JavaCalls::call_virtual(&result, class_loader, spec_klass, vmSymbols::loadClass_name(), vmSymbols::string_class_signature(), string, CHECK_NULL); } assert(result.get_type() == T_OBJECT, "just checking"); // 获取oop对象 oop obj = (oop) result.get_jobject(); // 如果不是基本类,则转换成对应的InstanceKlass if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) { InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(obj)); if (class_name == k->name()) { // 返回最终InstanceKlass return k; } } // Class is not found or has the wrong name, return NULL return NULL; } }复制代码
至此,JVM便完成了类型的InstanceKlass实例创建,这里两种加载方式中不管是通过bootstrap loader还是app(or自定义) loader均是 殊途同归,都会经历class文件的装载
、验证
、准备
、解析
、初始化
等操作
根据上述源码中我们已经了解到ClassLoader的实例就是Class#forName中的ClassLoader.getClassLoader(caller)
获取的类加载器,而class也是通过ClassLoader来管理对象的生命周期,但是ClassLoader又有三种不同类型的加载器,接下来就需要我们去谈谈双亲委派机制是它是怎么找到对应的加载器呢?
如果一个类加载器需要被加载,那么首先它会把这个类请求委派给父类加载器去完成,依次递归,当父加载器无法完成这个请求时,子类才会尝试去加载,这里父加载器不是java中的继承关系只是层级逻辑关系
从上述AppClassLoader和ExtClassLoader的实现都在sun.misc.Launcher类上,Launcher主要是在程序启动的时候加载主要的类加载器,我们看下Launcher加载的代码:
private static URLStreamHandlerFactory factory = new Launcher.Factory(); private static Launcher launcher = new Launcher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); private ClassLoader loader; private static URLStreamHandler fileHandler;复制代码
public Launcher() { Launcher.ExtClassLoader var1; try { //创建扩展类加载器 var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { //创建应用类加载器 this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } Thread.currentThread().setContextClassLoader(this.loader); String var2 = System.getProperty("java.security.manager"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { //应用类加载去尝试加载,然后去创建一个新的实例对象 //核心方法,双亲委派加载机制处理 var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } }复制代码
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException { if (instance == null) { Class var0 = Launcher.ExtClassLoader.class; synchronized(Launcher.ExtClassLoader.class) { if (instance == null) { instance = createExtClassLoader(); } } } return instance; }复制代码
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException { try { return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() { public Launcher.ExtClassLoader run() throws IOException { //加载路径String var0 = System.getProperty("java.ext.dirs"); File[] var1 = Launcher.ExtClassLoader.getExtDirs(); int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { MetaIndex.registerDirectory(var1[var3]); } return new Launcher.ExtClassLoader(var1); } }); } catch (PrivilegedActionException var1) { throw (IOException)var1.getException(); } }复制代码
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException { final String var1 = System.getProperty("java.class.path"); final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1); return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() { public Launcher.AppClassLoader run() { URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2); return new Launcher.AppClassLoader(var1x, var0); } }); }复制代码
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded //检查是否被加载过,如果内存中存在直接从内存获取,否则返回null Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { //尝试使用父加载器去加载 c = parent.loadClass(name, false); } else { //使用最顶层到父加载器加载Bootstrapclassloader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //如果父加载器都没加载成功,使用自己加载器加载 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }复制代码
使用了双亲委派模型为什么又要破坏双亲委派模型,其实理解起来很简单双亲委派解决了安全新和性能(避免重复加载 和 避免核心类被篡改)但是有一种情况下并不能满足现在的技术需要,列如目前要求对程序动态性,所谓对动态性(OSGI)是指:支持模块化的动态部署、支持模块的动态扩、动态化的设计、稳定高效的系统简单的来说就是服务器不需要重启,部署下就能使用,所以jdk开发人员就引入了线程上下文类加载器(Thread Context ClassLoader)
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。接下来简单看几个java原生spi示例参考dubbo官方文档:
我们定义一个接口,名称为 Robot。
public interface Robot { void sayHello(); }复制代码
接下来定义两个实现类,分别为 OptimusPrime 和 Bumblebee。
public class OptimusPrime implements Robot { @Override public void sayHello() { System.out.println("Hello, I am Optimus Prime."); } } public class Bumblebee implements Robot { @Override public void sayHello() { System.out.println("Hello, I am Bumblebee."); } }复制代码
接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 org.apache.spi.Robot。文件内容为实现类的全限定的类名,如下:
org.apache.spi.OptimusPrime org.apache.spi.Bumblebee复制代码
做好所需的准备工作,接下来编写代码进行测试。
public class JavaSPITest { @Test public void sayHello() throws Exception { ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class); System.out.println("Java SPI"); serviceLoader.forEach(Robot::sayHello); } }复制代码
最后来看一下测试结果,如下:
从测试结果可以看出,我们的两个实现类被成功的加载,并输出了相应的内容。关于 Java SPI 的演示先到这里,接下 Dubbo SPI自行去官方了解dubbo.apache.org/zh-cn/docs/…。
在这里感到遗憾是由于时间和自身能力有限不能把JNI和线程上下文类加载器这块相关示例代码我分享出来,但是我还是很开心从百忙之中抽出时间来和大家分享知识,在这次分享里可能结交到各界技术大牛,本次分享到此为止