当前位置: 首页 > news >正文

Java 类加载器与加载机制

文章目录

  • 1、类的生命周期
  • 2、类的加载过程
    • 2.1、加载(Loading)
    • 2.2、连接(Linking)
      • 2.2.1、验证(Verification)
      • 2.2.2、准备(Preparation)
      • 2.2.3、解析(Resolution)
    • 2.3、初始化(Initialization)
      • 2.3.1、使用static + final修饰的字段的显式赋值的操作,到底是在哪个阶段进行的赋值?
      • 2.3.2、什么时候对类进行初始化?
    • 2.4、总结
  • 3、类加载器
    • 3.1、类加载器分类
      • 3.1.1、BootstrapClassLoader
      • 3.1.2、ExtClassLoader
      • 3.1.3、AppClassLoader
    • 3.2、类加载机制
      • 3.2.1、双亲委托机制
      • 3.2.2、类加载流程
        • 3.2.2.1、代码实现
        • 3.2.2.2、加载流程

1、类的生命周期

类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段::加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)。

其中,加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)属于类加载过程。

在这里插入图片描述

2、类的加载过程

在 Java 中,类加载过程是指将 Java 类的字节码加载到内存中,并转换为 Java 虚拟机能够识别和执行的数据结构的过程。类加载是 Java 虚拟机执行 Java 程序的必要步骤之一,它负责加载程序中用到的类和接口。

根据上面的概述可知,JVM加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。

在这里插入图片描述

2.1、加载(Loading)

加载是“类加载”过程的第一阶段,为了不混淆这两个名字,接下来我都用装载(Loading)来叙述类加载过程的第一阶段。所谓装载,简而言之就是将Java类的字节码文件加载到机器内存中,并在内存中构建出Java类的原型即类模板Class对象。

在装载阶段,虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取其定义的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  3. 在Java堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口。

装载这一阶段主要是通过类加载器完成的,类加载器是另一个重要的知识点后面我会安排总结。每个 Java 类都有一个引用指向加载它的 ClassLoader。不过数组类不是通过 ClassLoader 创建的,而是 JVM 在需要的时候自动创建的,数组类通过getClassLoader()方法获取 ClassLoader 的时候和该数组的元素类型的 ClassLoader 是一致的。这就意味着我们可以自定义一个类加载器来加载类实现隔离可控。

2.2、连接(Linking)

链接阶段包括三个子阶段:验证、准备和解析。

2.2.1、验证(Verification)

验证阶段主要确保类的字节码符合 Java 虚拟机规范,以及不会危害 Java 虚拟机的安全。主要包括文件格式验证、元数据验证、字节码验证和符号引用验证等。

  • 文件格式验证:验证字节流是否符合 Class 文件格式的规范。例如:是否以 0xCAFEBABE 开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
  • 元数据验证:对字节码描述的信息进行语义分析(注意:对比 javac 编译阶段的语义分析),以保证其描述的信息符合 Java 语言规范的要求。例如:这个类是否有父类,除了 java.lang.Object 之外。
  • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:确保解析动作能正确执行。

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用 -Xverifynone 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

2.2.2、准备(Preparation)

准备阶段为类的静态变量分配内存,并设置默认初始值。这些变量在编译期间已经确定了初始值,但在此阶段还未赋值。静态变量也称为类变量,即被 static 关键字修饰的变量

这里所设置的初始值"通常情况"下是数据类型默认的零值(如 0、0L、null、false 等),而不是被在 Java 代码中被显式地赋予的值。比如我们定义了public static int n=666 ,那么 n 变量在准备阶段的初始值就是 0 而不是 666(初始化阶段才会赋值),因为在这个阶段并不会像初始化阶段中那样会有初始化或者我们自己编写的Java代码被执行。各个数据类型的零值如下所示:

数据类型零值(未赋值的默认值)
booleanfalse
byte(byte)0
short(short)0
int0
long0L
floato.of
double0.0d
char‘\u0000’
referencenull

我们前面特别强调是”通常情况“下,这就说明有特殊情况需要注意,比如给 n 变量加上了 final 关键字
public static final int n=6那么准备阶段 n 的值就被赋值为 6。还有以下几点需要注意:

  1. 对于同时被 static 和 final 修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;
  2. 只被 final 修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值。
  3. 只被 static 修饰的变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值
  4. 没有被 static 和 final 修饰的变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值
  5. 对于局部变量,没有赋值,编译错误,并不会根据对应的数据类型而被赋予默认的“空”值。
public class Test {private static final int a = 1;//必须在声明的时候就为其显式地赋值private static int b;//默认值 0private final int c;//在类初始化时显式地为其赋值private int d;//默认值 0private final int e = 3;//声明时显式赋值public Test(){c = 2;}public void test(){System.out.println(a);System.out.println(b);System.out.println(c);System.out.println(d);System.out.println(e);int f;System.out.println(f);//f没有赋值,编译不通过String g;System.out.println(g);//f没有赋值,编译不通过}public static void main(String[] args){new Test()

相关文章:

  • 详解 Flink Table API 和 Flink SQL 之函数
  • 计算机网络(3) 字节顺序:网络字节序与IPv4
  • Stack详解(含动画演示)
  • Hutool有哪些常用方法
  • 服务架构的设计原则
  • DS1338/PT7C4338串行实时时钟-国产兼容RS4C1338
  • 如何免费用 Qwen2 辅助你翻译与数据分析?
  • Excel根据身份证号提取信息
  • C语言详解(预编译)
  • App推广效果分析,Xinstall助力精准优化
  • 【wiki知识库】06.文档管理页面的添加--前端Vue部分
  • 记录pytest中场景执行的token异常处理问题
  • 加油卡APP开发,汽车加油便捷新方式
  • C++:调整数组顺序使奇数位于偶数前面【面试】
  • 创新共享经济:探索Web3对新商业模式的启迪
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • learning koa2.x
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Python socket服务器端、客户端传送信息
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Vue.js源码(2):初探List Rendering
  • WePY 在小程序性能调优上做出的探究
  • 从tcpdump抓包看TCP/IP协议
  • 力扣(LeetCode)56
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 配置 PM2 实现代码自动发布
  • 让你的分享飞起来——极光推出社会化分享组件
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • Nginx实现动静分离
  • PostgreSQL之连接数修改
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​第20课 在Android Native开发中加入新的C++类
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二)windows配置JDK环境
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (十)c52学习之旅-定时器实验
  • (十八)SpringBoot之发送QQ邮件
  • (十八)三元表达式和列表解析
  • (十五)使用Nexus创建Maven私服
  • (四)汇编语言——简单程序
  • (学习日记)2024.01.09
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (原創) 未来三学期想要修的课 (日記)
  • .bat批处理(六):替换字符串中匹配的子串
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .mysql secret在哪_MYSQL基本操作(上)
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • ::什么意思