Java 类加载机制

文章目录

  • 概述
  • 类的生命周期
  • 类加载的时机
  • 类加载的主要 5 个阶段
    • 加载
    • 验证
    • 准备
      • 准备阶段初始值的含义
    • 解析
      • 符号引用
      • 直接引用
      • 解析阶段的理解
      • 静态绑定与动态绑定
    • 初始化
  • 类加载器
    • 类加载器与类之间的关系
    • 类加载器的种类
  • 双亲委派机制
    • 双亲委派机制设计目的
  • 破坏双亲委派机制
    • 破坏双亲委派机制的方法
    • 破坏双亲委派机制的意义

概述

JVM 类加载机制,指的是把某个类加载到 JVM 中的整个流程。

类的生命周期

类从被加载到 JVM 中开始,到使用完毕卸载出内存为止,整个生命周期包括:

Created with Raphaël 2.3.0加载验证准备解析初始化使用卸载

其中,验证准备解析三个部分统称为连接阶段

类加载的时机

在一个类尚未被加载到 JVM 中时,在遇见以下几种情况时,必须立即对类进行加载:

  • 创建对象实例,访问类的静态字段或者静态方法时
  • 对类进行反射操作时
  • 当加载一个类时,如果发现其父类还没有加载,则需要先加载其父类
  • 当虚拟机启动时,需要立即加载要执行的主类(包含 main() 方法,要被执行的类)

类加载的主要 5 个阶段

加载

加载阶段,JVM 需要执行的操作主要有:

  • 通过一个类的全限定名来获取此类的二进制字节流(来源可能是 .class 文件,有可能是 jar 包,也有可能是运行时计算生成的,如动态代理生成的代理类二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在堆中生成一个代表这个类的 Class 对象,作为上一步中方法区的运行时数据结构的访问入口

验证

验证阶段的主要目的,是为了确保 Class 文件的二进制字节流中包含的信息是否符合当前虚拟机的要求,并且不会有危害虚拟机的行为。

验证阶段的逻辑策略是虚拟机保护自身安全的重要手段

验证阶段主要会完成以下四个阶段的检验动作:

  • 文件格式验证,如验证字节流的版本是否能被当前虚拟机处理
  • 元数据验证,如校验是否继承了 final 修饰的类
  • 字节码验证,如校验跳转指令是否正确
  • 符号引用验证,验证符号引用中描述的全限定名能否找到对应的类

准备

准备阶段是 JVM 为类变量(静态变量)分配内存并设置类变量初始值的阶段,这些类变量所使用的内存都将在方法区中进行分配。
注意,这里说的是给类变量设置初始值,实例变量(非类变量)将会在对象实例化时随着实例化好的对象一起分配在堆中。
划重点:准备阶段是设置类变量(静态变量)初始值的阶段。

准备阶段初始值的含义

初始值,需要分为常量变量和非常量变量(是否被 final 关键字修饰)

  • 非常量变量(未被 final 修饰),则初始值为对应类型的零值(例如 char 类型的零值为 `\u0000`
  • 常量变量(被 final 修饰),则初始值为代码中指定的值
    • 例如:private static final long NUMBER = 888L,准备阶段将会给 NUMBER 变量赋初始值为 888L

解析

解析阶段,是 JVM 将常量池中的符号引用替换为直接引用的过程。

符号引用

符号引用,即代码中代表被引用资源的字符串,例如一个方法的名称,一个类名,或者一个静态变量的名称。

直接引用

直接引用,即可以直接访问到引用目标的指针,相对偏移量,或者一个句柄。

解析阶段的理解

符号引用替换成直接引用,意思就是说把代码中代表被引用资源的字符串,替换成对应的能直接访问到这些被引用资源的资源(指针,相对偏移量或者句柄)。例如:

public static int maxValueOfInt() {//在解析阶段,将会把 Integer.MAX_VALUE 这个符号引用转化成能直接访问到 Integer 类的 MAX_VALUE 静态变量的指针return Integer.MAX_VALUE;
}

静态绑定与动态绑定

在一个类的加载过程中,解析阶段是可以先完成一部分,在类的整个加载过程完成后再继续完成剩余部分的,这是为了支持动态绑定
在这里,绑定的意思就是把符号引用替换成直接引用的过程,所以

  • 静态绑定,即在类加载过程中就可以确定符号引用所代表的被引用资源,所以直接在解析阶段就可以完成符号引用替换成直接引用的过程
  • 动态绑定,即需要在程序运行过程中才可以确定符号引用所代表的被引用资源,所以需要在整个类加载过程完成后再进行符号引用替换成直接引用的过程

动态绑定常见于当某个父类或接口类有多个子类或实现类时,JVM 需要在程序时机运行阶段才能确定引用的时哪个子类或实现类

初始化

初始化阶段,是对类从加载到使用阶段前的最后一个阶段,也是执行类构造器 <clinit>() 方法的阶段。

  • <clinit>() 方法是由编译器自动收集类中的所有类变量的赋值操作,以及静态块中的语句合并生成的
  • 如果一个类中没有静态语句块,也没有类变量,那么可能不会生成 <clinit>() 方法

综上所属,初始化阶段,就是执行 <clinit>() 方法的阶段,就是在执行类中的所有非常量类变量的赋值,以及静态块中语句的过程。

类加载器

通过一个类的全限定名来获取描述该类的二进制字节流,执行这个动作的程序就称为类加载器

类加载器与类之间的关系

在判定两个类是否相等时,需要满足以下两个条件:

  • 全限定名相同
  • 类加载器相同,即两个类是由同一个类加载器来加载的

可以看出,对于一个任意的类,都需要由加载它的类加载器和这个类本身来一起共同确定在 JVM 中的唯一性

类加载器的种类

类加载器主要有以下几种:

  • 启动类加载器:Bootstrap Class Loader,由 C++ 来实现的,负责加载 JRElib 目录下的核心类库,如 rt.jarcharsets.jar
  • 扩展类加载器:Extension Class Loader,由 ExtClassLoader 来实现的,负责加载 JREext 目录下的扩展类包
  • 应用程序类加载器:Application Class Loader,由 AppClassLoader 来实现的,负责加载 classpath 下的类包
  • 自定义类加载器:User Class Loader,由用户自行实现,负责加载用户自定义路径下的包

请注意,类加载器之间并没有继承关系,使用的是组合关系

双亲委派机制

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。
因此,所有的类加载请求都应该被传送到最顶层的启动类加载器中,只有当父加载器不能完成加载请求时,子加载器才会尝试自己去完成加载。

双亲委派机制设计目的

  • 沙箱安全机制:保证核心类库不会被随意加载,可以防止用户破坏核心类库的功能,保证了 JVM 的运行安全
  • 避免类的重复加载:可以保证一个类只会被一个类加载器加载,从而不会出现类相同而类加载器不同的情况,避免了类的重复加载

破坏双亲委派机制

如果一个类加载器收到了类加载的请求,它没有委派给父类加载器加载,而是直接尝试自己去完成加载,这就叫破坏了双亲委派机制。

破坏双亲委派机制的方法

自定义一个类加载器,然后重写 loadClass(String name, boolean resolve) 方法,把其中的委派逻辑给干掉,直接调用 findClass() 方法即可破坏双亲委派机制:

     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

破坏双亲委派机制的意义

双亲委派机制可以保证相同的类不会被重复加载,但是在某些场景下,相同的类需要以不同版本的形式加载进 JVM
例如,同一个 Tomcat 容器中,可能会需要部署多个 Web 应用

  • 不同的 Web 应用可能会依赖同一个第三方类库的不同版本
  • Tomcat 容器本身也需要依赖一些第三方类库,不能跟 Web 应用使用的第三方类库混淆,否则很可能会给容器本身带来安全问题

所以这个时候,就需要破坏双亲委派机制,让不同的应用程序自己加载自己所需要的类库。
Tomcat 为不同的 Web 应用都创建了不同的 WebAppClassLoader,在 WebAppClassLoader 中实现了破坏双亲委派机制的逻辑,使不同应用间所依赖的类库版本互相隔离

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557274.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java –什么是瞬态字段?

在Java中&#xff0c; transient字段在序列化过程中被排除。 简而言之&#xff0c;当我们将对象保存到文件中&#xff08;序列化&#xff09;时&#xff0c;所有transient字段都将被忽略。 1. POJO 瞬态 复查以下Person类&#xff1b; 薪水领域是transient 。 public class …

JVM 内存模型与内存分配方式

文章目录JVM 内存模型概述基于分代收集理论设计的垃圾收集器所管理的堆结构方法区的演变内存分配划分内存的方法划分内存时如何解决并发问题对象栈上分配基于分代收集理论的垃圾收集器管理下的内存分配规则对象优先分配在 Eden 区大对象直接进入老年代长期存活的对象将逐步进入…

image pil 图像保存_如何利用python中的PIL库做图像处理?

自从这个世界上出现了Python编程&#xff0c;一切都好像有了新的思路与进展&#xff0c;比如人工智能&#xff0c;还有我们常用的PS&#xff0c;你可知道Python也可以做图像处理&#xff0c;用的就是PIL库&#xff0c;还没有用过的&#xff0c;还没有发现的&#xff0c;还没有实…

GSON详解

GSON GSON弥补了JSON的许多不足的地方&#xff0c;在实际应用中更加适用于Java开发。在这里&#xff0c;我们主要讲解的是利用GSON来操作java对象和json数据之间的相互转换&#xff0c;包括了常见的对象序列化和反序列化的知识。 一、前言 因为json有2种类型&#xff1a; 一…

机器人 瓷砖墙面清洗_墙壁清洁机器人解析

1第一章绪论1.1课题的背景、目的及意义[1]壁面清洗爬壁机器人属于移动式服务机器人的一种&#xff0c;可在垂直壁面或顶部移动&#xff0c;完成其外表面的清洗作业。在工业机器人问世30多年后的今天&#xff0c;它已被世人看作是一种生产工具&#xff0c;在制造、装配及最近在服…

内存回收算法与 Hot Spot 算法实现细节

文章目录内存回收算法概述对象存活判定算法引用计数算法可达性分析算法垃圾收集算法分代收集理论标记-清除算法标记-复制算法半区复制算法Appel 式复制算法Appel 式复制算法的逃生门设计标记-整理算法HotSpot 虚拟机实现细节GC Root 枚举Hot Spot 实现 GC Root 枚举安全点与安全…

link st 量产工具_ST-Link资料03_ST-Link固件升级、驱动下载安装方法

说明&#xff1a;本文原创作者『strongerHuang』本文首发于微信公众号『嵌入式专栏』&#xff0c;同时也更新在我的个人网站&#xff1a;EmbeddedDevelop一、写在前面前两篇文章讲述的都是关于ST-Link的一些理论知识&#xff0c;建议初学者看看&#xff1a;ST-Link资料01_ST-Li…

Java 主流垃圾收集器

文章目录垃圾收集器概述Serial 与 Serial Old 垃圾收集器Serial 与 Serial Old 垃圾收集器总结ParNew 垃圾收集器Parallel Scavenge 垃圾收集器Parallel Scavenge 的吞吐量控制参数Parallel Scavenge 的自适应调节策略Parallel Scavenge 垃圾收集器总结ParNew 和 Parallel Scav…

SSM 框架整合 spring 发送邮件功能实现!

基于SSM的 spring 发送邮件的实现 由于考虑到项目中需要&#xff0c;如果程序出现异常或其它问题&#xff0c;可以发送邮件来及时提醒后台维护人员处>理。所以目前考虑使用JavaMail来实现邮件发送&#xff01;如下&#xff0c;是我整理的一些内容&#xff0c;做个笔记记录下…

Java 故障处理与性能监控工具

文章目录概述基础工具jpsjstatjinfojmapjhatjstack高级工具VisualVMVisualVM 的主要功能ArthasGC Easy概述 在使用 Java 语言进行开发的过程中&#xff0c;我们很可能会遇到各种程序问题。 比如&#xff0c;可能会遇见程序突然就静止不动了&#xff0c;但是程序进程依然显示在…

SSM整合框架实现发送邮件功能

SSM整合框架实现发送邮件功能 1.导入发送邮件的依赖 <!-- 发送邮件jar包--><!--spring支持--><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.0.0.RELE…

ap接口 php_2018年小米高级 PHP 工程师面试题(模拟考试卷)

点击蓝字关注我们&#xff01;每天获取最新的编程小知识&#xff01;源 / php中文网 源 / www.php.cn在面试之前多看看有关公司的面试资料&#xff0c;对之后的面试会很有帮助。今天就给大家带来2018年小米高级 PHP 工程师面试题(模拟考试卷)&#xff0c;有着一定的参考价…

composer 设置版本号_Composer依赖管理 – PHP的利器

别再到处搜PHP类扩展包了&#xff0c;对于现代语言而言&#xff0c;包管理器基本上是标配。Java 有 Maven&#xff0c;Python 有 pip&#xff0c;Ruby 有 gem&#xff0c;Nodejs 有 npm。PHP 的则是 PEAR&#xff0c;不过 PEAR 坑不少&#xff1a;依赖处理容易出问题配置非常复…

LeetCode.31 下一个排列

题目描述 实现获取下一个排列的算法&#xff0c;算法需要将给定的数组&#xff0c;重新排列成下一个更大的排列&#xff08;即组合出下一个更大的数字&#xff09;。 如果不存在下一个更大的排列&#xff0c;则将数字重新排列成最小的排列。 题目思路 我们可以使用如下的思路…

SpringBoot2.5.4发送邮件4种方式

一.准备 在创建SpringBoot项目 二、选择依赖 选择依赖时 发现其选择依赖时有邮件发送与Springboot整合的jar包&#xff0c;我们勾选即可 如果未勾选也不要紧&#xff0c;咱们手动导入 <dependency><groupId>org.projectlombok</groupId><artifactId…

手游传奇刷元宝_传奇手游 平民制霸刀刀爆元宝!

新轩辕神途手游游戏介绍新轩辕神途是一款玩法种类十分丰富多样的神途手游&#xff0c;游戏内拥有放置挂机升级玩法&#xff0c;玩家不用浪费时间工作上学都能自动升级打宝&#xff0c;更有十分庞大的世界地图等你来探索&#xff0c;十分靠谱&#xff0c;更受欢迎&#xff0c;爆…

MySQL 逻辑架构与常用的存储引擎

文章目录概述逻辑架构示意图Server 层功能模块连接器查询缓存分析器优化器执行器存储引擎层InnoDBInnoDB 主要特性InnoDB 引擎下的查询过程MyISAMMyISAM 的主要特性MyISAM 引擎下的查询过程InnoDB 和 MyISAM 的对比概述 MySQL 是我们平时开发中最常用的关系型数据库&#xff0…

java 阿里云接口实现发送短信验证码

1.先去阿里云开通短信服务&#xff1a; 2.添加模板及签名&#xff1a;需要审核&#xff0c;个人账户审核就几分钟就OK 先解释一下模板及签名&#xff1a; 标准参照&#xff1a;https://help.aliyun.com/document_detail/55324.html?spm5176.sms-sign.0.0.765c1cbeNhvWBZ 去…

faiss python安装_faiss入门

Faiss入门篇假定Faiss已经被安装&#xff0c;若未安装可参考小编安装编译篇https://zhuanlan.zhihu.com/p/78689463。本篇小编基于Faiss的官方wiki实例展开&#xff0c;旨在让大家快速入门Faiss。Faiss底层用c实现并为用户提供python接口&#xff0c;本篇我们以python示例Faiss…

MySQL 索引底层数据结构实现

文章目录概述讨论范围查询数据结构查询数据结构种类及其高性能查询原理MySQL 索引的底层数据结构MySQL 索引的需求分析选择 MySQL 索引的底层数据结构B- 树和 B 树的对比MySQL 索引的底层数据结构揭秘概述 MySQL 的索引是存储引擎用于快速找到记录的一种数据结构&#xff0c;是…