图解类加载器和双亲委派机制,一看就懂

简介: 我们都知道Java代码会被编译成class文件,在class文件中描述了该类的各种信息,class类最终需要被加载到虚拟机中才能运行和使用。虚拟机把Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成虚拟机可以直接使用的Java类型,这就是虚拟机的类加载机制。

网络异常,图片无法展示
|


相信我,微信搜《Java鱼仔》真的可以变强


(一)概述


我们都知道Java代码会被编译成class文件,在class文件中描述了该类的各种信息,class类最终需要被加载到虚拟机中才能运行和使用。


虚拟机把Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成虚拟机可以直接使用的Java类型,这就是虚拟机的类加载机制。


(二)类加载的过程


一个类从被加载到卸载出内存,一共包含下面七个阶段:


加载、验证、准备、解析、初始化、使用、卸载


网络异常,图片无法展示
|
加载的来源有以下部分:


1、本地磁盘


2、网络下载的.class文件


3、war,jar下加载.class文件


4、从专门的数据库中读取.class文件(少见)


5、将java源文件动态编译成class文件,典型的就是动态代理,通过运行时生成class文件

加载的过程是通过类加载器实现的。有关类加载的其他过程我会在下一章中介绍。


(三)类加载器的分类


类加载器分为系统级别和用户级别:


系统级别的类加载器有:


1、启动类加载器(底层使用C++实现)


2、扩展类加载器(底层使用java实现,是ClassLoader的子类)


3、应用程序类加载器(底层使用java实现,是ClassLoader的子类)


用户级别的类加载器我们统一称为自定义类加载器。


3.1 启动类加载器


首先我们来看看启动类加载器加载了哪些类,启动类加载器负责加载sun.boot.class.path:


publicstaticvoidbootClassLoaderLoadingPath(){
//获取启动列加载器加载的目录StringbootStrapLoadingPath=System.getProperty("sun.boot.class.path");
//把加载的目录转为集合List<String>bootLoadingPathList=Arrays.asList(bootStrapLoadingPath.split(";"));
for (StringbootPath:bootLoadingPathList){
System.out.println("启动类加载器加载的目录:"+bootPath);
    }
}

通过上面的代码我们可以获取到启动类加载器所加载的类:


网络异常,图片无法展示
|


3.2 拓展类加载器


扩展类加载器加载负责加载java.ext.dirs,我们同样写一段代码去加载它:

publicstaticvoidextClassLoaderLoadingPath(){
//获取启动列加载器加载的目录StringbootStrapLoadingPath=System.getProperty("java.ext.dirs");
//把加载的目录转为集合List<String>bootLoadingPathList=Arrays.asList(bootStrapLoadingPath.split(";"));
for (StringbootPath:bootLoadingPathList){
System.out.println("拓展类加载器加载的目录:"+bootPath);
    }
}

可以看到,除了加载了JDK目录下的ext外,还加载了Sun目录下的ext


网络异常,图片无法展示
|

3.3 应用程序类加载器


最后是应用类加载器,它负责加载java.class.path:

publicstaticvoidappClassLoaderLoadingPath(){
//获取启动列加载器加载的目录StringbootStrapLoadingPath=System.getProperty("java.class.path");
//把加载的目录转为集合List<String>bootLoadingPathList=Arrays.asList(bootStrapLoadingPath.split(";"));
for (StringbootPath:bootLoadingPathList){
System.out.println("应用程序类加载器加载的目录:"+bootPath);
    }
}

(四)双亲委派模型


所谓双亲委派模型,就是指一个类接收到类加载请求后,会把这个请求依次传递给父类加载器(如果还有的话),如果顶层的父类加载器可以加载,就成功返回,如果无法加载,再依次给子加载器去加载。


网络异常,图片无法展示
|

我们先通过代码来看一下类加载器的层级结构:

publicclassClassLoaderPath {
publicstaticvoidmain(String[] args) {
System.out.println(ClassLoaderPath.class.getClassLoader());
System.out.println(ClassLoaderPath.class.getClassLoader().getParent());
System.out.println(ClassLoaderPath.class.getClassLoader().getParent().getParent());
    }
}

编写一个类,依次输出这个类的类加载器,父类加载器,父类的父类加载器

网络异常,图片无法展示
|


可以看到首先是应用程序类加载器,它的父类是扩展类加载器,扩展类加载器的父类输出了一个null,这个null会去调用启动类加载器。如果你不信,我们看源码:


ClassLoader类


网络异常,图片无法展示
|


接着从父类加载器往下调用findClass,如果可以加载,就直接返回class,如果不能加载,就依次向下。如果到了自定义加载器还是无法被加载,就会抛出ClassNotFound异常。


我画了一个流程图来展示双亲委派模型的全过程:


网络异常,图片无法展示
|

双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载,也保证了 Java 的核心 API 不被篡改。


(五)破坏双亲委派


双亲委派模型并不是绝对的,spi机制就可以打破双亲委派模型。


首先我们需要了解什么是spi,spi(Service Provider Interface)是一种服务发现机制,Java在核心库中定义了许多接口,并且针对这些接口给出调用逻辑,但是并未给出具体的实现。开发者要做的就是定制一个实现类,在 META-INF/services 中注册实现类信息,以供核心类库使用。最典型的就是JDBC。


Java提供了一个Driver接口用于驱动各个厂商的数据库连接,Driver类位于JAVA_HOME中jre/lib/rt.jar中,应该由Bootstrap类加载器进行加载。根据类加载机制,当被加载的类引用了另外一个类的时候,虚拟机就会使用加载该类的类加载器加载被引用的类,因此如果其他数据库厂商定制了Driver的实现类之后,按理说也得把这个实现类放到启动类加载器加载的目录下,这显然是很不合理的。


于是Java提供了spi机制,即使Driver由启动类加载器去加载,但是他可以让线程上下文加载器(Thread Context ClassLoader)去请求子类加载器去完成加载,默认是应用程序类加载器。但是这确实破坏了类加载机制。



相关文章
|
18小时前
|
存储 Java 编译器
类加载机制和双亲委派机制
类加载机制和双亲委派机制
|
18小时前
|
监控 安全 前端开发
JVM工作原理与实战(十二):打破双亲委派机制-自定义类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、自定义类加载器等内容。
19 1
|
18小时前
|
安全 前端开发 Java
大厂面试题详解:聊一下双亲委派机制
字节跳动大厂面试题详解:聊一下双亲委派机制
28 0
|
18小时前
|
安全 前端开发 Java
【JVM】双亲委派机制详细解读(通俗易懂)
【JVM】双亲委派机制详细解读(通俗易懂)
170 0
|
10月前
|
存储 缓存 安全
JVM面试专题-双亲委派机制(父类委托机制)
JVM面试专题-什么是双亲委派机制(父类委托机制)?如何打破双亲委派机制?双亲委派机制的优缺点?什么是沙箱安全机制呢?
159 1
|
7月前
|
前端开发 Java C++
【面试题精讲】JVM-打破双亲委派机制-自定义类加载器
【面试题精讲】JVM-打破双亲委派机制-自定义类加载器
|
7月前
|
Java 数据库连接 数据库
【面试题精讲】JVM-打破双亲委派机制-线程上下文类加载器
【面试题精讲】JVM-打破双亲委派机制-线程上下文类加载器
|
7月前
|
Arthas 前端开发 安全
【面试题精讲】JVM-双亲委派机制
【面试题精讲】JVM-双亲委派机制
|
12月前
|
前端开发 安全 Java
JVM-白话聊一聊JVM类加载和双亲委派机制源码解析
JVM-白话聊一聊JVM类加载和双亲委派机制源码解析
52 0
|
前端开发 Java
简述ClassLoader双亲委派模式
简述ClassLoader双亲委派模式
97 0