java类加载器,加载原理简述

本文只是对类加载器的加载原理做一简述,如有不对,请指正。

1. 加载之前

  1. 原理图
    在这里插入图片描述
  2. 原理简述
    2.1 自己编写一个Myclass.java文件。
    2.2 在运行该文件时,首先通过javac Myclass.java 将该文件编译成Myclass.class文件(默认会在当前目录下)。
    2.3 通过类加载器将Myclass.class文件加载到JVM中的运行时数据区,再通过执行引擎将加载到运行时数据区的字节码文件

2. 加载时

  1. 原理图
    在这里插入图片描述
  2. 加载源码
    首先判断当前类加载器是否有父类,如果有就调用父类加载器进行加载,否则就掉用Bootstrap类加载器进行加载,当Bootstrap类加载器进行加载失败时,抛出类为发现异常,就会调用当前类加载器进行加载。
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false); // 委托父类加载
                    } else {
                        c = findBootstrapClassOrNull(name); // 委托Bootstrap加载
                    }
                } 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;
        }
    }
  1. 原理简述
    当类加载器加载Myclass.class时,会自底向上检查该Myclass.class是否已经被加载过,假如在Extension class loader处发现,该Myclass.class已经被加载过,则Myclass.class将不会被再次加载,而是会使用已经加载过的Myclass.class。加入检查到Bootstrap class loader 处也没有发现该Myclass.class被加载过,此时则会自顶向下进行尝试加载,首先Bootstrap class loader尝试在java_home/lib目录下找Myclass.class是否存在,存在就会被加载并完成Myclass.class的加载行为,否则继续向下尝试加载,当找到Custom class loader都没有找到Myclass.class时,则会抛出ClassNoFoundException异常。

3. 类加载之后

  1. 原理图
    在这里插入图片描述
  2. 原理简述
    2.1 加载完成后,首先检查加载的类文件是否符合java规范和虚拟机规范
    2.2 分配所需内存,确定类属性、方法等所需的数据结构
    2.3 将常量池中的符号引用改变为直接引用
    2.4 初始化类的局部变量,为静态域赋值,执行静态初始化块。
  3. 限制
    3.1 下层的加载器能够看到上层加载器中的类,反之则不行
    3.2 类加载器可以加载一个类,但是不能卸载一个类。但是类加载器可以被删除或者被创建

4. 双亲委派模型是什么?

如果一个类加载器收到一个加载某个类的请求,先去查找该类是否被已经加载过,如果被加载过就直接返回,否则该类加载器不会去加载该类,而是把该请求层层委托给父类加载器,每一层都是如此,直至递归到最顶层的类加载器(启动类加载器),只有当父类加载器在其搜索范围内无法找到所需的类时,子类加载器才会尝试去加载类。

5. JDBC为什么要破坏双亲委派模型?

因为类加载器受到加载范围受限,在某些情况下父类加载器无法加载到需要的文件,这时候就需要委托子类加载器去加载class文件。
JDBC的**Driver接口定义在JDK中,其实现是由各个数据库的服务商来提供,**比如MySql驱动包。DriverManager类中要加载各个实现了Driver接口的类,然后进行管理,但是DriverManager位于$JAVA_HOME中的jre/lib/rt.jar包,由于BootStrap类加载器来进行加载,而其Driver接口的实现类是位于服务商提供的jar包中的,**根据类加载器限制,当被装载的类引用另外一个类的时候,虚拟机就会使用装载第一个类的类加载器来加载被引用的类。**也就是说BootStrap类加载器还要去加载jar包中的Driver接口的实现类。我们知道BootStrap类加载器默认只负责加载$JAVA_HOME中jre/lib/tr.jar中的class文件,所以Driver接口的实现类根本不在这个路径下,因此需要有子类加载器去加载Driver接口的具体实现类。这样就破坏了双亲委派模型。
那么父类加载器怎么通过子类加载器去加载类文件呢,线程上下文类加载器可以通过Thread.setContThread.setContextClassLoaser()方法设置,如果不设置默认会从父类继承,一般默认使用的是应用程序类加载器。通过
Thread.currentThread().getContextClassLoader()来获取子类类加载器

6. Tomcat为什么要破坏双亲委派模型?

在这里插入图片描述
每个Tomcat的webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。
事实上,tomcat之所以造了一堆自己的classloader,大概有三个目的:

  1. 热部署
  2. 与jvm一样的安全性问题,使用单独的classloader去装载tomcat自身的类库,以免其他恶意破坏。
    https://blog.csdn.net/qq_38182963/article/details/78660779
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值