Java中的类加载器ClassLoader

43 篇文章 1 订阅
本文探讨了Java类加载的层次结构,包括Bootstrap、Extension和System类加载器,重点剖析了双亲委派模式的工作原理和优势,以及类加载器间的安全关系。同时,解释了类与类加载器的关系及class文件的显式与隐式加载区别。
摘要由CSDN通过智能技术生成

类加载机制的层次结构

每个编写的 .java 拓展名类文件都存储着需要执行的程序逻辑,这些 .java 文件经过 Java 编译器编译成拓展名为 .class 的文件,.class 文件中保存着 Java 代码经转换后的虚拟机指令,当需要使用某个类时,虚拟机将会加载它的 .class 文件,并创建对应的 class 对象,将 class 文件加载到虚拟机的内存,这个过程称为类加载,这里我们需要了解一下类加载的过程,如下

在这里插入图片描述

  • 加载:类加载过程的第一个阶段,通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个 Class 对象
  • 验证:目的在于确保 Class 文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证
  • 准备:为类变量(即 static 修饰的字段变量)分配内存并且设置该类变量的初始值即 0(如 static int i = 5; 这里只将 i 初始化为 0,至于 5 的值将在初始化时赋值),这里不包含用 final 修饰的 static,因为 final 在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到 Java 堆中
  • 解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用
  • 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的 static 变量将会在这个阶段赋值,成员变量也将被初始化)

这便是类加载的 5 个过程,而类加载器的任务是根据一个类的全限定名来读取此类的二进制字节流到 JVM 中,然后转换为一个与目标类对应的 java.lang.Class 对象实例。在虚拟机提供了 3 种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器),下面分别介绍

启动 Bootstrap 类加载器

启动类加载器主要加载的是 JVM 自身需要的类,这个类加载使用 C++ 语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib 路径下的核心类库或 -Xbootclasspath 参数指定的路径下的 jar 包加载到内存中,注意必由于虚拟机是按照文件名识别加载 jar 包的,如 rt.jar,如果文件名不被虚拟机识别,即使把 jar 包丢到 lib 目录下也是没有作用的(出于安全考虑,Bootstrap 启动类加载器只加载包名为 javajavaxsun 等开头的类)

扩展 Extension 类加载器

扩展类加载器是指 Sun 公司实现的 sun.misc.Launcher$ExtClassLoader 类,由 Java 语言实现的,是 Launcher 的静态内部类,它负责加载 <JAVA_HOME>/lib/ext 目录下或者由系统变量 -Djava.ext.dir 指定位路径中的类库,开发者可以直接使用标准扩展类加载器

系统 System 类加载器

也称应用程序加载器是指 Sun 公司实现的 sun.misc.Launcher$AppClassLoader。它负责加载系统类路径 java -classpath-D java.class.path 指定路径下的类库,也就是我们经常用到的 classpath 路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过 ClassLoader#getSystemClassLoader() 方法可以获取到该类加载器

在日常开发中,类的加载几乎是由上述 3 种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的 class 文件加载到内存生成 class 对象,而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它

双亲委派模式

双亲委派模式工作原理

双亲委派模式要求:除了顶层的 Bootstrap 类加载器外,其余的类加载器都应当有自己的父类加载器。请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码,类加载器间的关系如下

在这里插入图片描述
双亲委派模式是在 Java 1.2 后引入的,其工作原理的是:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的Bootstrap 类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这活我也干不了时,儿子才自己想办法去完成,这不就是传说中的实力坑爹啊?那么采用这种模式有啥用呢?

双亲委派模式优势

采用双亲委派模式的是好处

  • Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子类加载器再加载一次
  • 其次是考虑到安全因素,Java 核心 API 中定义类型不会被随意替换,假设通过网络传递一个名为 java.lang.Integer 的类,通过双亲委托模式传递到 Bootstrap 类加载器,而 Bootstrap 类加载器在核心 Java API 发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的 java.lang.Integer,而直接返回已加载过的 Integer.class,这样便可以防止核心 API 库被随意篡改

可能你会想,如果我们在 classpath 路径下自定义一个名为 java.lang.SingleInterge 类(该类是胡编的)呢?该类并不存在 java.lang 中,经过双亲委托模式,传递到 Bootstrap 类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为 java.lang 是核心 API 包,需要访问权限,强制加载将会报出如下异常

java.lang.SecurityException: Prohibited package name: java.lang

所以无论如何都无法加载成功的。下面了解几个 Java 中定义的类加载器及其双亲委派模式的实现,它们类图关系如下

在这里插入图片描述
从图可以看出顶层的类加载器是 ClassLoader 类,它是一个抽象类,其后所有的类加载器都继承自 ClassLoader(不包括 Bootstrap 类加载器)

类加载器之间的关系

  • Bootstrap 类加载器,由 C++ 实现,没有父类
  • 拓展 Extension 类加载器(ExtClassLoader),由 Java 语言实现,父类加载器为 null
  • System 类加载器(AppClassLoader),由 Java 语言实现,父类加载器为 ExtClassLoader
  • 自定义类加载器,父类加载器肯定为 AppClassLoader

类与类加载器

类与类加载器

JVM 中表示两个 class 对象是否为同一个类对象,存在两个必要条件

  • 类的完整类名必须一致,包括包名
  • 加载这个类的 ClassLoader (指 ClassLoader 实例对象)必须相同

也就是说,在 JVM 中,即使这个两个类对象(class 对象)来源同一个 Class 文件,被同一个虚拟机所加载,但只要加载它们的 ClassLoader 实例对象不同,那么这两个类对象也是不相等的,这是因为不同的 ClassLoader 实例对象都拥有不同的独立的类名称空间,所以加载的 class 对象也会存在不同的类名空间中

class 文件的显式加载与隐式加载

所谓 class 文件的显式加载与隐式加载的方式:JVM 加载 class 文件到内存的方式。

  • 显式加载:在代码中通过调用 ClassLoader 加载 class 对象,如直接使用Class.forName(name)this.getClass().getClassLoader().loadClass() 加载 class 对象
  • 隐式加载则是不直接在代码中调用 ClassLoader 的方法加载 class 对象,而是通过虚拟机自动加载到内存中,如在加载某个类的 class 文件时,该类的 class 文件中引用了另外一个类的对象,此时额外引用的类将通过 JVM 自动加载到内存中

转载:https://blog.csdn.net/javazejian/article/details/73413292

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值