二. jvm核心类加载器
类主要通过类加载器来加载, java里面有如下几种类加载器
1. 引导类加载器:负责加载支撑JVM运行的, 位于jre目录的lib目录下的核心类. 比如:rt.jar, charset.jar等,
2. 扩展类加载器: 负责加载支撑JVM运行的, 位于jre目录的lib/ext扩展目录中的jar包
3. 应用程序类加载器: 负责加载classPath路径下的类包, 主要加载自己写的类
4. 自定义类加载器: 负责加载用户自定义路径下的类包
引导类加载器是由C++帮我们实现的, 然后c++语言会通过一个Launcher类将扩展类加载器(ExtClassLoader)和应用程序类加载器(AppClassLoader)构造出来, 并且把他们之间的关系构建好.
例1:
package com.lxl.jvm; import sun.misc.Launcher; import java.net.URL; public class TestJDKClassLoader { public static void main(String[] args) { /** * 第一个: String 是jdk自身自带的类, 所以, 他的类加载器是引导类加载器 * 第二个: 加密类的classloader, 这是jdk扩展包的一个类 * 第三个: 是我们当前自己定义的类, 会被应用类加载器加载 */ System.out.println(String.class.getClassLoader()); System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName()); System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName()); } }
我们来看这个简单的代码, 运行结果:
null sun.misc.Launcher$ExtClassLoader sun.misc.Launcher$AppClassLoader
第一个: String 是jdk自身自带的类, 所以, 他的类加载器是引导类加载器
第二个: 加密类的classloader, 这是jdk扩展包的一个类, jdk扩展包里面使用的是extClassLoader类加载器加载的
第三个: 是我们当前自己定义的类, 会被AppClassLoader应用程序加载器加载.
我们看到ExtClassLoader和AppClassLoader都是Launcher类的一部分. 那Launcher类是什么东西呢?
上面有提到, Launcher类是jvm启动的时候由C++调用启动的一个类.
那么,第一个bootstrap引导类加载器, 那引导类加载器返回的为什么是null呢?
因为bootstrap引导类加载器, 他不是java的对象, 他是c++生成的对象, 所以这里是看不到的
例2: ExtClassLoader和AppClassLoader是怎么生成的?
从这个图中我们可以看出,C++调用java创建JVM启动器, 其中一个启动器是Launcher,他实际是调用了sun.misc.Launcher的getLauncher()方法. 那我们就从这个方法入手看看到底是如何运行的?
我们看到Lanucher.java类是在核心的rt.jar包里的
我们看到getLauncher()类直接返回了launcher. 而launcher是一个静态对象变量, 这是一个单例模式
C++通过getLauncher()-->直接返回了lanucher对象, 而launcher对象是在构建类的时候就已经初始化好了. 那么,初始化的时候做了哪些操作呢?接下来看看他的构造方法.
在构造方法里, 首先定义了一个ExtClassLoader. 这是一个扩展类加载器, 扩展类加载器调用的是getExtClassLoader(). 接下来看一看getExtClassLoader这个方法做了什么?
在这里, 判断当前对象是否初始化过, 如果没有, 那么就创建一个ExtClassLoader()对象, 看看createExtClassLoader()这个方法做了什么事呢?
doPrivileged是一个权限校验的操作, 我们可以先不用管, 直接看最后一句, return new Launcher.ExtClassLoader(var1). 直接new了一个ExtClassLoader, 其中参数是var1, 代表的是ext扩展目录下的文件.
在ExtClassLoader(File[] var1)这个方法中, 这里第一步就是调用了父类的super构造方法. 而ExtClassLoader继承了谁呢? 我么你可以看到他继承了URLClassLoader.
而URLClassLoader是干什么用的呢? 其实联想一下大概能够猜数来, 这里有一些文件路径, 通过文件路径加载class类.
我们继续看调用的super(parent), 我们继续往下走, 就会看到一下内容
这里设置了ExtClassLoader的parent是谁? 注意看的话,我们发现, ExtClassLoader的parent类是null.
这就是传递过来的parent类加载器, 那么这里的parent类加载器为什么是null呢? 因为, ExtClassLoader的父类加载器是谁呢? 他是Bootstrap ClassLoader. 而BootStrap ClassLoader是C++的类加载器, 我们不能直接调用它, 所以, 设置为null.
其实, ExtClassLoader在初始化阶段就是调用了ExtClassLoader方法, 初始化了ExtClassLoader类
接下来,我们回到Launcher的构造方法, 看看Launcher接下来又做了什么?
可以看到, 接下来调了AppClassLoader的getAppClassLoader(var1), 这个方法. 需要注意一下的是var1这个参数. var1是谁呢? 向上看, 可以看到var1是ExtClassLoader.
这是AppClassLoader, 应用程序类加载器, 这个类是加载我们自己定义的类的类加载器. 他也是继承自URLClassLoader.
我们来看看getAppClassLoader(final ClassLoader var0)方法. 这个方法的参数就是上面传递过来的ExtClassLoader
这里第一句话就是获取当前项目的class 文件路径, 然后将其转换为URL. 并调用了Launcher.AppClassLoader(var1x, var0), 其中var1x是class类所在的路径集合, var0是扩展的类加载器ExtClassLoader, 接下来, 我们进入到这个方法里看一看
AppClassLoader直接调用了其父类的构造方法, 参数是class类路径集合, 和ExtClassLoader
最后, 我们看到, 将ExtClassLoader传递给了parent变量. 这是定义在ClassLoader中的属性, 而ClassLoader类是所有类加载器的父类. 因此, 我们也可以看到AppClassLoader的父类加载器是ExtClassLoader
同时, 我们也看到了, C++在启动JVM的时候, 调用了Launcher启动类, 这个启动类同时加载了ExtClassLoader和AppClassLoader.
public static void main(String[] args) { ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); ClassLoader extClassLoader = appClassLoader.getParent(); ClassLoader bootstrapClassLoad = extClassLoader.getParent(); System.out.println("bootstrap class loader: " + bootstrapClassLoad); System.out.println("ext class loader " + extClassLoader); System.out.println("app class loader "+ appClassLoader); }
通过这个demo, 我们也可以看出, appClassLoader的父类是extClassLoader, extClassLoader的父类是bootstrapClassLoader
输出结果:
bootstrap class loader: null ext class loader sun.misc.Launcher$ExtClassLoader@2a84aee7 app class loader sun.misc.Launcher$AppClassLoader@18b4aac2