JVM 中的类加载器:概述

类加载器是 Java 虚拟机 (JVM) 的重要组成部分,但许多开发人员认为它们很神秘。本文旨在通过提供对 JVM 中类加载如何工作的基本理解来揭开这个主题的神秘面纱。

什么是类加载器
在 Java 虚拟机 (JVM) 中,类是动态加载的,并通过称为类加载的过程找到。类加载是将类从其二进制表示形式(通常是 .class 文件)加载到内存中的过程,以便 JVM 可以执行它。这就是我们需要类加载器的地方。类加载器用于将 .class 文件加载到内存中。

如何在 JVM 中加载类

类的加载分三个步骤进行:

创建和加载步骤。 发生的第一件事是使用类加载器加载类文件。有两种类型的类装入器:由 JVM 提供的 Bootstrap 类装入器和用户定义的类装入器。(有关类加载器的更多详细信息将在下一章中介绍)然后,创建一个类的实例,并使该类可供 JVM 进一步执行。可以在 Java 虚拟机规范中找到详细的分步算法。java.lang.Class

在类准备好执行之前的链接步骤。JVM 需要执行许多准备操作,其中包括验证和准备要执行的类。链接步骤如下:

字节码验证。

验证可确保类或接口的二进制表示形式在结构上是正确的,并且未损坏。否则,将不会链接类文件,并且将引发 VerifyError 错误。可以通过该选项关闭验证。关闭验证可以加快 JVM 的启动速度,但禁用字节码验证会破坏 Java 的安全性保证。为什么不禁用字节码验证?-noverify

制备。

为静态字段分配RAM,并使用默认值进行初始化。

符号链接的解析。

由于对字段、方法和其他类的所有引用都是符号式的,因此为了执行该类,您需要将引用转换为内部表示。

初始化步骤。成功加载并链接类后,可以对其进行初始化。在此阶段,调用静态类初始值设定项或静态变量初始值设定项,确保静态初始化块只执行一次,并且静态变量初始化正确。

此外,值得记住的是,Java 实现了类的延迟(或延迟)加载。这意味着,在应用程序显式引用已加载类的引用字段之前,不会执行这些引用字段的类加载。换言之,字符引用分辨率是可选的,默认情况下不会发生。

Classloader 特性
类加载器有三个值得记住的重要特性:

委托模型:当被要求查找类或资源时,类装入器会将类或资源的搜索委托给其父类装入器,然后再尝试查找类或资源本身。
能见度: 由父类装入器装入的类对其子类装入器可见,但由子类装入器装入的类对其父类装入器或子类装入器不可见。
唯一性:在 Java 中,一个类被唯一标识,因为同一个类可能由两个不同的类加载器加载。 它有助于为不同的类加载器定义不同的保护和访问策略。ClassLoader + ClassClass A loaded by ClassLoader A != Class A loaded by ClassLoader B
类加载器关系
我们应该记住,Java 中的类是按需加载的。也就是说,仅在请求时加载类。

如您所知,用 Java 编写的每个程序的入口点都是 method。主要方法是加载第一个类的地方。所有随后加载的类都由已加载并正在运行的类加载。public static void main(String[] args)

当正在运行的程序请求某个类时,系统类加载器会在应用程序类路径中搜索该类。如果未找到该类,则搜索 Platform Class Loader,如果仍未找到,则搜索 Bootstrap Class Loader。

如果在父类装入器中找到所请求的类,那么该类装入器将装入该类。否则,系统类装入器将加载该类。如果以前未加载过该类,则类加载器会将其加载到内存中,并创建表示已加载类的类对象的新实例。

需要注意的是,类加载层次结构本质上是分层的,每个类加载器都有一个父类加载器。这种父子关系确保每个类装入器只负责装入自己的类,并将父类的装入委托给其父类装入器。

不同类型的类加载器
JVM 中的类加载机制不会只使用一个类加载器。每个 Java 程序至少有三个类加载器:

Bootstrap(原始)类加载器: 这是根类加载器,负责在 Java 标准库(也称为 Java 运行时环境或 JRE)中加载核心 Java 类,例如 java.lang.Object 和其他类。它是在本机代码中实现的,并且是 JVM 本身的一部分。尽管每个类装入器都有自己的对象,但没有对应于 Bootstrap 类装入器的此类对象。例如,如果您运行这行代码,您将得到 。ClassLoaderString.class.getClassLoader()null
扩展类加载器: 这个类加载器负责从扩展目录(比如 JRE 安装中的 jre/lib/ext 目录)加载类,并且是 Bootstrap 类加载器的子类。您还可以通过 system 属性指定扩展目录的位置。java.ext.dirs
系统(应用程序)类装入器: 这是加载特定于应用程序的类的类加载器,通常从运行 Java 应用程序时指定的类路径中加载。类路径可以包含目录、JAR 文件和其他资源。可以使用 CLASSPATH 环境变量、-classpath 或 -cp 命令行选项来设置类路径。System/Application Class Loader 也是用 Java 实现的,并且是 Extension Class Loader 的子类。
类加载器以及与它们相关的类加载器会随着时间的推移而变化
在上一节中,我们已经看到了在 Java 9 修订它之前一直存在于 Java 中的类加载器层次结构。

自 Java 9 以来,新的类加载器层次结构如下所示:

Bootstrap(原始)类加载器:这是根类加载器,负责加载核心 Java 类,例如 Java 标准库(也称为 Java 运行时环境或 JRE)中的其他类。它是在本机代码中实现的,并且是 JVM 本身的一部分。尽管每个类装入器都有自己的对象,但没有对应于 Bootstrap 类装入器的此类对象,并且通常表示为 ,并且没有父级。例如,如果您运行这行代码,您将得到 。java.lang.ObjectClassLoadernullString.class.getClassLoader()null
平台类加载器(前身为扩展类加载器):Java SE 平台中的所有类都保证通过平台类加载器可见。仅仅因为一个类通过平台类加载器是可见的,并不意味着该类实际上是由平台类加载器定义的。Java SE 平台中的某些类由 Platform Class Loader 定义,而其他类由 Bootstrap Class Loader 定义。应用程序不应依赖于哪个类加载器定义了哪个平台类。
系统(应用程序)类装入器:这是加载特定于应用程序的类的类加载器,通常从运行 Java 应用程序时指定的类路径中加载。类路径可以包含目录、JAR 文件和其他资源。可以使用 CLASSPATH 环境变量、-classpath 或 -cp 命令行选项来设置类路径。System/Application Class Loader 也是用 Java 实现的,并且是 Extension Class Loader 的子类。
Java 9 中引入了更多与类加载器相关的更改,即:

应用程序类加载器不再是 的实例,而是一个内部类。现在,有一些类本身包含三个内置类加载器的实现。如:URLClassLoaderClassLoaders
BootClassLoader
PlatformClassLoader
AppClassLoader
但是,Bootstrap 类加载器应该通过类而不是类来使用。BootLoaderClassLoaders
Extension Class Loader 已重命名为 Platform Class Loader。Java 8 Extension Class Loader 和 Java 9 Platform Class Loader 之间的实质性区别在于,Platform Class Loader 不再是 的实例。但在大多数情况下,Platform Class Loader 等同于过去所谓的 Extension Class Loader。重命名它的一个动机是已删除扩展机制,我们将在下一段中讨论。URLClassLoader
移除了扩展机制:在 Java 9 之前的版本中,扩展机制允许运行时环境查找和加载扩展类,而无需在类路径中明确提及它们。但是,在 JDK 9 中,此机制已被删除。要使用扩展类,请确保其 JAR 文件包含在类路径中。
删除了rt.jar和tools.jar
rt.jar包含基本 Java 运行时环境的所有已编译类文件。
tools.jar包含 JDK 所需的所有工具,但不包括 JRE(javac、javadoc、javap)所需的所有工具。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值