java类加载机制与类加载器

类加载运行全过程

当我们java程序在运行一个main方法的时候,需要经历以下这些步骤(这里以一个User类为例)

在这里插入图片描述
1.当需要加载User.class的时候,首先会是AppClassLoader,AppClassLoader会判断自己是否有父类加载器,很显然是有的(ExtClassLoader),所以AppClassLoader会把加载User.class的事委托他的父类加载器去加载。
2.当ExtClassLoader加载User.calss的时候,会和上一步一样,判断他自己是否有父类加载器,Ext的父类加载器是BootstrapClassLoader,所以他会把加载User.class的事委托他的父类加载器去加载。
3.当BootsrapClassLoader加载User.class的时候,会判断自己是否会有父类加载器,BootstrapClassLoader是顶级类加载器,所以他没有父类加载器了。这个时候他会校验自己是否应该加载User.class这个类,前面介绍了,BootstrapClassLoader只负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等,很明显,User.class不会被这个类加载器所加载。
4.告诉ExtClassLoader,让他自己去加载User.class(其实这里在代码层次就是返回一个null,让他的子类加载器知道他没有加载这个类)
5.ExtClassLoader准备加载User.class,他会判断User.class是否该由自己加载,ExtClassLoader只加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包,所以ExtClassLoader不会加载User.Class
6.告诉AppClassLoader,让他自己去加载User.class
7.当AppClassLoader准备加载User.class的时候,他会校验是否由自己加载,AppClassLoader负责加载ClassPath路径下的类包,就是加载我们自己写的那些类,所以AppClassLoader会加载User.class。

而加载类,主要是通过loadClass去完成的,在loadClass的类加载过程会经历以下几个步骤
加载>>>>验证>>>>准备>>>>解析>>>>初始化>>>>>使用>>>>>卸载
**加载:**这一步是指通过类的全限定名来定位到这个文件(这里并不一定需要是class文件),并将我们的文件从通过io读入到内存中,这里并不是直接加载到了jvm中,等通过验证的第一阶段才会进入到jvm的方法区(只有使用到一个类的时候,才会去加载。例如我们执行main()方法时,创建一个对象时,不管是通过new,还是反射,都会进行这一步,当然只有第一次创建的时候才会加载)在加载阶段会在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。
**验证:**校验字节码的准确性(很容易理解,假如我们修改了Class字节码文件里的内容,肯定不能校验通过。该阶段分为好几步,1.文件格式验证;(这个阶段是基于二进制字节流验证的,只有通过这个验证,字节流才会进入内存的方法区存储)2.元数据验证;3.字节码验证;4.符号引用验证(后面三个验证阶段都是基于方法区的存储结构进行的))
**准备:**给类的静态变量分配内存,并赋予默认值(例如int赋值为0,对象赋值null)
**解析:**将符号引用替换为直接引用,这个过程会将一些静态方法替换为方法在内存中的地址。
**初始化:**对类的静态变量赋予初始值,执行静态代码块。注意这里是指静态变量,如果是静态常量(被final修饰的变量)在准备阶段就已经赋予了值。

上面介绍的是一个类加载的全过程,下面我们分几块来介绍。

首先是类加载器,在java中,类加载器一共有四种,分别是:
**引导类加载器(启动类加载器)**BootstrapClassLoader:负责加载JRE lib目录下的核心类库
扩展类加载器ExtClassLoader:负责加载JRE lib/ext目录下的类库
程序类加载器AppClassLoader:负责加载ClassPath路径下的类包,例如我们自己写的类。
自定义类加载器:负责加载我们指定路径的类。

public class ClassLoaderTest {
    public static void main(String[] args) {
        //因为BootstrapClassLoader是C++实现的,所以这里打印的为null
        System.out.println(String.class.getClassLoader());
        //com.sun.nio.zipfs.ZipPath位于lib/ext下的zipfs.jar中
        System.out.println(com.sun.nio.zipfs.ZipPath.class.getClassLoader());
        System.out.println(ClassLoaderTest.class.getClassLoader());
    }
}
null
sun.misc.Launcher$ExtClassLoader@5b1d2887
sun.misc.Launcher$AppClassLoader@18b4aac2

接下来我们看下每个加载器的父加载器

public class ClassLoaderParentTest {
    public static void main(String[] args) {
        //因为BootstrapClassLoader是C++实现的,所以这里打印的为null
        try {
            System.out.println(String.class.getClassLoader().getParent());
        }catch (Exception e){
            System.out.println(e);
        }
        //因为ExtClassLoader的父加载器为BootstrapClassLoader,所以输出的为null
        System.out.println(com.sun.nio.zipfs.ZipPath.class.getClassLoader().getParent());
        System.out.println(ClassLoaderTest.class.getClassLoader().getParent());
    }
}
java.lang.NullPointerException
null
sun.misc.Launcher$ExtClassLoader@5b1d2887

从上面的结果我们可以看出,BootstrapClassLoader是最顶级的类加载器,他是C++实现的,所以我们打印出来的jvm内存地址为null。
ExtClassLoader的父类加载器输出的为null,因为他的父类加载器是BootstrapClassLoader,所以打印的为null。
AppClassLoader的父类加载器是ExtClassLoader.

口说无凭,上代码。
我们看看在Launcher中,这块是如何定义与实现的。

首先我们看看Launcher类的构造函数(这里删除了很多代码,只留下了两行我们关注的)

public Launcher() throws Exception {
        // 定义扩展类加载器
        Launcher.ExtClassLoader var1 = Launcher.ExtClassLoader.getExtClassLoader();
        // 创建appClassLoader,这里传入的参数是ExtClassLoader
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    }

接下来我们看看ExtClassLoader的构造函数

public ExtClassLoader(File[] var1) throws IOException {
			//这里的第二个参数,最终会调到this.parent = (ClassLoader) null;
            super(getExtURLs(var1), (ClassLoader) null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

再看看我们的AppClassLoader的构造函数

AppClassLoader(URL[] var1, ClassLoader var2) {
			//这里的var2就是在Launcher构造函数中getAppClassLoader中的参数,所以他是ExtClassLoader
			//最终会调this.parent = var2;
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

所以说,ExtCalssLoader的父加载器是BootstrapClassLoader,AppClassLoader的父加载器是ExtClassLoader.
从代码中可以看出,这里的父子关系,并不是继承关系,只是一个组合关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值