JAVA类加载机制详解

上一篇文章我们简单说了一下类的创建过程,但是如果JVM需要加载类,会经过哪些具体的过程呢?下面我们就来谈一谈。

要了解加载类的过程,我们就必须要了解类加载器。

在很多初学者刚听到类加载器的时候觉得很高大上,其实类加载器无非就是和我们平时说的Class对象而已。

在java里面我们经常用到的类加载器可能就是以下几种:

  • 引导(启动)类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等 bootstrtload------>jre/lib目录
  • 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JA类包 extClassLoad------->jre/lib/ext目录下
  • 应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类 appClassLoad------>classPath目录下
  • 自定义加载器:负责加载用户自定义路径下的类包

类加载原理:主要使用双亲委派机制

双亲委派机制:一个类进来先由应用类加载器------->扩展类加载器------->启动类加载器 然后启动类建加载器尝试加载 因为启动类加载器去加载jre包下面的lib目录 而我们写的类在classpath下面 启动类加载器加载不到就会像扩展类加载器(默认加载ext包下面的)去下发------------>应用类加载器 (从claaPath下面查找)

总结就是:双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载

双亲委派详情具体执行过程: 调用findClassLoad 判断有没有被加载 如果没有被加载 判断parent是否为空 如果为空调用bootstrap引导类加载器,extClassLoad里面是没有findClass()方法的 然后去调用URLCalssLoad里面的findClass()方法 然后去找target下面对应的类文件加载到(调用define方法()这个方法比较复杂,没有仔细研究过)jvm内存里面去 此时肯定加载不到因为类再target下面不在extclasss下面 返回为null 此时又会返回appclassload下面 找到对应appclassload的findclass()方法加载也会调用urlclassload的逻辑同时也会调用define()(调用一系列本地方法执行加载加载的过程)方法 此时就会加载到对应的.class文件

双亲委派源码: 重要的方法ClassLoad 类里面的loadClass 里面实现findClass()方法 真正加载类的逻辑

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
//  loadClass()实现双亲委派机制
{
    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);
                }
            } 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. 沙箱安全机制:自己写的java.lang.String.class类不会被加载(包名和类名和jdk自带的一样 运行不成功),这样便可以防止核心API库被随意篡改(自己在自定义类加载器里面要加载java.lang....类似于和核心包里面系统的包名称 jvm直接报一个安全异常) jvm加载到和自己核心类库里面相同的类必须用自己类库里面的类。

  2. 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次(父类就直接返回了),保证被加载类的唯一性(相同包名类名只加载一份)。

类加载机制还有一个全盘负责委托机制,所谓的"全盘负责"是指当一个ClassLoder装载一个类时,假如这个类也用到其他的类了 这个类也会由这个ClassLoader加载,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入。

相信很多人面试的时候一般都会问到关于双亲委派机制的问题。同时可能面试官还会问一个怎么打破这个机制。如果问到这个问题基本上就是问双亲委派的源码了,在上面的代码里面也提到了,双亲委派机制核心就是两个核心:ClassLoad 类里面的loadClass 里面实现findClass()方法 真正加载类的逻辑,基本上就是重写重写loadClass()方法即可。

为什么要打破双亲委派机制?

(双亲委派加载在以下情况下不能解决问题(tomcat为例) 1,假如web容器里部署两套应用程序,不同的应用程序可能依赖第三方库的版本不一样,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离 一个spring版本4 一个是spring5 如果4先加载了 如果5再使用同样的库 那么拿到的就是4加载的那个 就会出现问题,部署在同一个web容器中相同的类库相同的版本可以共享, 2, web容器也有自己依赖的类库,不能与应用程序的类库混淆。3,基于安全考虑,应该让容器的类库和程序的类库隔离开来。 这些问题是双亲委派机制解决不了的 双亲委派机制要求同一个类同一个包名只能加载一份)

tomcat就是典型的打破了双亲委派:

tomcat 为了实现隔离性 没有遵循jvm双亲委派机制 tomcat自定义了很多类加载器 其中的一个WebAppClassLoder 每一个应用程序webappClassLoader都是相互隔离的加载自己的目录下(jir)的class文件,不会传递给父类加载器,打破了双亲委派机制。tomcat里面每一个war包都会对应生成一个appClassLoad。

 

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Java反射机制是指在运行时动态地获取一个类的信息,并可以操作类的属性、方法和构造器等。Java反射机制可以使程序员在运行时动态地调用类的方法和属性,扩展类的功能,并可以实现注解、工厂模式以及框架开发等。 Java反射机制的原理如下:首先,Java编译器将Java源代码编译为字节码文件,字节码文件中包含着类的信息,这些信息包括类的名称、方法、属性和构造器等等。接着,Java虚拟机将字节码文件加载到内存中,然后通过类加载器将类加载到内存中形成一个类对象,这个类对象可以操作字节码文件中的信息。 使用Java反射机制的过程如下:首先获取类对象,通过类对象来获取类的构造器、属性、方法等信息,然后调用构造器来创建对象,通过属性获取和设置类的成员属性,通过方法调用类的方法等。 Java反射机制的优点是可以在运行时动态地得到类的信息,使得程序员在程序运行时能够对类进行更加灵活的操作,并可以使得程序更加通用化,同时也存在着一定的性能问题,因为Java反射机制需要Java虚拟机进行一定的额外处理,所以在程序运行时需要进行额外的时间和资源消耗。 总之,Java反射机制是Java语言的一项重要特性,在Java开发中广泛应用,在代码编写、框架开发以及API开发中具有重要作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值