JAVA双亲委派JAVA虚拟机类加载机制双亲委派机制

JAVA虚拟机类加载机制

Java虚拟机把.class类加载到内存,并且对数据进行校验,解析和初始化。形成能被虚拟机直接使用的Java对象,这就是类加载机制。

类加载过程如下:
Java类加载流程图
为支持运行时绑定,解析过程可以在初始化完成在进行,其他加载过程必须按照上图流程进行。

Java中 java.lang.Class的forName()和Java.lang.ClassLoader的loadClass()

  • 联系:forName()方法使用的是调用者的类加载器loadClass()方法类加载类的。
  • 区别:Class.forName(String)载入class时,会完整的执行加载、连接、验证、准备、解析、初始化的流程。如果调用的是ClassLoader.loadClass(String)载入class时,会执行加载、连接、验证、准备、解析流程,不会执行最后的初始化流程,这是类的static块没有被执行,需要在类第一次实例化是执行,比如第一次执行Class.newInstance()操作。

今天重点是讲解双亲委派机制,如果想了解更多类加载流程请点击这里

双亲委派机制

Java类加载器关系图

  • BootstrapClassLoader:启动类加载器,属于JAVA对顶层类加载器。主要加载Java核心包下的类,如rt.jar。由C语音编写,不属于Java类,所以无法获取它的引用,JVM启动时会通过BootstrapClassLoader类加载器加载rt.jar包下的class文件,如String,Integer等。由于是最顶层类加载器,所以是没有父类加载器的。
  • ExtentionClassLoader:扩展类加载器,属于第二级类加载器。主要加载系统目录java.ext.dirs下的jar包和class文件,是JVM虚拟机的系统类加载器。其父类是BootstrapClassLoader。
  • AppClassLoader:应用类加载器。加载当前应用类路径ClassPath下的所有class类,其父类是ExtentionClassLoader。
  • xxxClassLoader:自定义类加载器。加载自定义class文件。其父类是AppClassLoader。

加载流程
一般情况下加载一个类是从AppClassLoader类加载器开始的,

  1. AppClassLoader类加载器查找资源时,先查看当前目标类是否已经被加载。如果没有被加载过,那么首先要做的不是查看自己的包路径下是否有当前类,而是直接委托给父类加载器ExtentionClassLoader。
  2. ExtentionClassLoader类加载器查找资源时,也不是首先查看自己包路径下是否有目标类文件,而是直接委托给父类加载器BootstrapClassLoader类加载器。
  3. BootstrapClassLoader是最顶层类加载器,所以会在rt.jar包中找有没有目标类,如果找到就加载类并返回。如果没有找到就会到ExtentionClassLoader类加载器进行加载。
  4. 如果ExtentionClassLoader类加载器找到目标类文件,那么就加载类并返回。如果没有那么就回到AppClassLoader类加载器进行加载。
  5. 最后如果其他类加载都没有找到就会回到自己本身类加载,如果找到目标类文件并加载成功,就直接返回。
  6. 如果AppClassLoader也没有找到目标类,那么就跑异常。

图解
在这里插入图片描述

双亲委派工作过程
如果一个类加载器收到类的加载任务时,它首先不会自己去加载这个类,而是把这个类委派给父类加载器去完成,每一层的类加载器都是如此。因此所有的类加载请求都会传到顶层类加载器中,只有当父类加载器反馈没有完成加载(没有找到目标类),子加载器才会尝试自己去进行加载。

双亲委派作用
可以避免类的重复加载,保证安全性,防止系统jar包被本地覆盖替换。

面试提问

  1. 什么是双亲委派机制?
    类加载机制分为启动类加载器、扩展类加载器、应用类加载器、自定义类加载器。当一个类加载器收到加载类的请求后不会自己去加载,而是交给父级类加载器去加载,如果父类加载器找不到类没有加载然后才会由自己加载。
  2. 为什么需要双亲委派机制?没有双亲委派机制会有什么问题?
    防止类重复加载,保证安全性。如果没有双亲委派机制那么JDK的核心包下类可 能会被恶意替换加载。
  3. 父加载器和子加载器是继承关系吗?
    不是继承关系,它们通过组合模式来复用类加载器
  4. 双亲委派机制怎么实现的?
    在classLoader加载类中loadClass方法中实现。先判断类有没有被加载,如果没 有则交给父类加载器进行加载,如果父类加载器为null,则使用启动类加载器作为父 类进行加载,如果父类加载器没有加载,那么会调用自己类加载器的findClass进行 加载。
  5. 我能不能主动破坏这种机制?怎么破坏?
    可以。自定义类加载器并重写其中的loadClass方法。
  6. 为什么重新loadClass()可以破坏双亲委派机制,这个方法和findClass和defineClass有 何区别?
    loadClass方法进行类加载,默认的双亲委派加载就这这个方法里面实现。 findClass方法根据名称或位置进行加载类。 defineClass方法吧字节码转化为class
    eg:自定义类加载器并重写loadClass可以破坏双亲委派机制。如果需要自定义一个类 加载器但不破坏双亲委派,那就继承classloader并重写findClass。因为在JDK1.2后如果父 类加载器加载失败后会调用自己类的findclass方法进行加载;
  7. 说一说你知道的破坏双亲委派的例子?
    双亲委派是JDK1.2后出现的,所以之前的类加载器就没有双亲委派;JDBC, Tomact,为了实现热插拔热部署的工具,模块化技术应用等都破坏了双亲委派机制;
  8. 为什么JDBC需要破坏双亲委派机制?
    JDBC通过引入ThreadContextClassLoader(线程上下文加载器)的方式 破坏了双亲委派机制;
  9. 为什么Tomcat需要破坏双亲委派机制?
    Tomcat破坏双亲委派机制提供隔离机制,为每个web容器提供单独一个 webAppClassLoader加载器;
  10. 模块化技术的理解?
    在JDK1.9中,整个JDK都基于模块化进行构建,以前的rt.jar,tool.jar等都被拆分 数十个模块,编译的时候只编译被加载到的模块,同时各个类加载器各司其职,只负责加载自己的模块。
  • 最后虚心学习,共同进步。-_-