JVM之【类加载系统】
目录
前言
类加载过程
类加载
执行过程
加载阶段
连接阶段
初始化阶段
类加载器
BootstrapClassLoader
ExtClassLoader
AppClassLoader
类加载器之间的关系
双亲委派机制
核心思想
好处
源码分析
类加载器之间的父子层级关系
双亲委派的体现
前言
上文中提到,Java源文件通过前端编译器生成.class字节码文件,字节码文件通过JVM中的类加载系统加载进内存再交由执行引擎处理,本文主要介绍类加载系统
类加载过程
类加载
当JVM需要用到某个类时,会加载它的.class文件,加载之后会创建对应的Class对象
执行过程
整个执行过程分为阶段:加载阶段、连接阶段、初始化阶段
加载阶段
通过完全限定名查找class文件二进制数据并加载进内存的过程,可以划分为三步
- 通过完全限定名定位到.class文件,并获取其二进制字节流数据
- 字节流的静态存储结构变为运行时数据结构
- 堆空间中创建一个Class对象,作为程序访问其数据的入口
连接阶段
分为验证、准备、解析三步,其中解析可能会在初始化之后执行
- 验证:检测class字节流数据是否符合虚拟机的要求,确保不会危害虚拟机自身的安全
- 准备:为类中声明的静态变量分配内存空间,并将其初始化为默认值
- 解析:把类中对常量池内的符号引用转化为直接引用的过程
初始化阶段
执行<clinit>(),对类的静态变量赋予指定的值(声明时指定的值或静态代码块赋予的值)
类加载器
JVM提供了三种类加载器,分别为BootstrapClassLoader、ExtClassLoader、AppClassLoader;当然也可以自定义实现类加载器
BootstrapClassLoader
是用C++实现的一种类加载器,是JVM的一部分,负责将如下两种位置下的类库加载到内存
- <JAVA_HOME>\lib
- -Xbootclasspath 指定
ExtClassLoader
是由sun公司实现的,位于sun.misc.Launcher中的一个静态内部类
package sun.misc;
// ...public class Launcher {// ...// 扩展类加载器ExtClassLoaderstatic class ExtClassLoader extends URLClassLoader {// ...}// ...
}
负责将如下两种位置下的类库加载到内存
- <JAVA_HOME>\lib\ext
- -Djava.ext.dir 指定
AppClassLoader
也是由sun公司实现,位于sun.misc.Launcher中的一个静态内部类,负责将类路径下的类库加载到内存
package sun.misc;
// ...public class Launcher {// ...// 应用程序类加载器AppClassLoaderstatic class AppClassLoader extends URLClassLoader {// ...}// ...
}
类加载器之间的关系
BootstrapClassLoader是在JVM启动时初始化的,负责加载ExtClassLoader,并将ExtClassLoader的父加载器设置为BootstrapClassLoader;接着BootstrapClassLoader加载AppClassLoader,并将AppClassLoader的父加载器设置为ExtClassLoader,自定义类加载器的父加载器为AppClassLoader
package classloader;public class CustomClassLoader extends ClassLoader {public static void main(String[] args) {CustomClassLoader customClassLoader = new CustomClassLoader();System.out.println("自定义类加载器:" + customClassLoader);ClassLoader loader = customClassLoader.getParent();System.out.println("自定义类加载器的父加载器:" + loader);loader = loader.getParent();System.out.println("AppClassLoader的父加载器:" + loader);loader = loader.getParent();System.out.println("ExtClassLoader的父加载器:" + loader);}
}
BootstrapClassLoader是由C++实现的,所以获取的是null
双亲委派机制
核心思想
类加载器从上至下为:BootstrapClassLoader -> ExtClassLoader -> AppClassLoader
- 自下而上检查类是否已经被加载
- 从上至下尝试加载类
好处
- 避免一个类在不同层级的类加载器重复加载
- 保障Java核心类的安全性,如通过网络传输一个java.lang.String类,需要被加载时,通过双亲委派机制最终找到BootstrapClassLoader后,发现该类已经被加载从而不会加载传输过来的java.lang.String类,直接返回BootstrapClassLoader加载的String.class,有效防止Java核心API被篡改
源码分析
类加载器之间的父子层级关系
类加载器之间的父子层级关系体现在Launcher类的无参构造器
package sun.misc;public class Launcher {// ...private ClassLoader loader;// ...// 无参构造器public Launcher() {ExtClassLoader var1;try {// 实例化ExtClassLoadervar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {// 实例化AppClassLoader,并将ExtClassLoader作为其父加载器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}// 将AppClassLoader作为线程上下文类加载器Thread.currentThread().setContextClassLoader(this.loader);// ...}static class AppClassLoader extends URLClassLoader {final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {// ...// 传过来的var0作为parentreturn new AppClassLoader(var1x, var0);}});}AppClassLoader(URL[] var1, ClassLoader var2) {// var2作为parentsuper(var1, var2, Launcher.factory);this.ucp.initLookupCache(this);}// ...}// ExtClassLoader,单例模式的体现static class ExtClassLoader extends URLClassLoader {private static volatile ExtClassLoader instance;public static ExtClassLoader getExtClassLoader() throws IOException {if (instance == null) {Class var0 = ExtClassLoader.class;synchronized(ExtClassLoader.class) {if (instance == null) {instance = createExtClassLoader();}}}return instance;}private static ExtClassLoader createExtClassLoader() throws IOException {try {return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {public ExtClassLoader run() throws IOException {File[] var1 = Launcher.ExtClassLoader.getExtDirs();int var2 = var1.length;for(int var3 = 0; var3 < var2; ++var3) {MetaIndex.registerDirectory(var1[var3]);}return new ExtClassLoader(var1);}});} catch (PrivilegedActionException var1) {throw (IOException)var1.getException();}}// ...}
}
双亲委派的体现
双亲委派模型体现在ClassLoader类中的loadClass方法
package java.lang;public abstract class ClassLoader {// ...protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 通过完全限定名查看自己是否已经加载过Class<?> c = findLoadedClass(name);// 如果自己没有加载过if (c == null) {long t0 = System.nanoTime();try {// 如果父加载器不为空,就将类加载请求委托给父加载器if (parent != null) {c = parent.loadClass(name, false);} else {// 父加载器为空就代表当前类加载器为ExtClassLoader,就将类加载请求委托给BootstrapClassLoaderc = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// ...// 父加载器加载失败时,调用该方法完成类加载c = findClass(name);// ...}}// 加载时是否需要解析if (resolve) {resolveClass(c);}// 返回加载后生成的Class对象return c;}}// ...// 该方法是留给子类重写的protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}// ...
}