加载流程

JVM类加载器加载流程如下:

步骤操作
1编译,    使用命令 java HelloWorld.java 生成 HelloWorld.class
2加载,    在硬盘查找并读取HelloWorld.class字节码文件,加载这个类
3验证,    验证字节码的准确性
4解析,    将符号引用替换为直接引用
5初始化, 对类的静态变量初始化为指定的值,执行静态代码块

可以用画图表示如下:

JVM类加载器加载流程

双亲委派机制

双亲委派机制是当类加载器需要加载某一个.class字节码文件时,则首先会把这个任务委托给他的上级类加载器,递归这个操作,如果上级没有加载该.class文件,自己才会去加载这个.class。这是一种任务委派模式。

原理:

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的启动类加载器;
  3. 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

说的不那么拗口一点,向上依次查找可用加载器,直到找到顶层加载器也可用。

类加载器分类:

序号加载器加载内容
1用户自定义加载器自定义目录
2AppClassLoader环境变量 (用户定义的类或者第三方api类)
3ExtClassLoaderjre/lib/ext 扩展 (加载-Djava.ext.dirs选项指定的目录)
4BootstrapClassLoaderjre核心库 (如HaspMap类、java.lang.String类)

如图所示:

JVM类加载器双亲委派

其中用户自定义加载器为最底层加载器,它的上级是AppClassLoader加载器,再上级是ExtClassLoader加载器,最上层加载器是BootstrapClassLoader加载器。

双亲委派机制的好处

使用双亲委派机制的好处是可以避免类的重复加载,保护程序安全,防止核心API被随意篡改例如自定义类java.lang.string。

例子:

自定义一个String类java.lang.String

package java.lang;

public class String {
    public String toString() {
        return "hello";
    }
}

调用java.lang.string

public class HelloWorld {
    public static void main(String[] args) {
        String s = new String();
        System.out.println("s类加载器" + s.getClass().getClassLoader());
        System.out.println("输出的字符串" + s.toString());
    }
}
输出
s类加载器null
输出的字符串

输出的字符串没有我们想看到的hello字符串。调用的还是系统类库。String的类加载器使用的是BootstrapClassLoader,对Java不可见,所以返回了null 。

因为jvm类加载器是自底向上查找到的。最终还是会在BootstrapClassLoader加载器中加载java.lang.String。