首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java类加载过程

Java类加载过程

原创
作者头像
Mr.Shelby
发布2025-06-30 20:49:02
发布2025-06-30 20:49:02
940
举报

类加载过程

1.加载

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

第一点可以非常灵活,Class文件可以从ZIP压缩包中读取——JAR,WAR的基础。从网络中获取。运行时动态计算生成。

从加密文件获取

额外讲一下数组与非数组对象在加载中的区别。

数组对象不通过Class文件生成,由JVM自动生生成。若是引用类型如String[]则通过类加载器加载元素类。若是基本数据类型int[],内置于JVM中,在JVM运行时生成。而非数组类则通过ClassLoader处理。

内存分配的区别

普通对象的内存分配通过JVM的堆内存进行,JVM在堆上为对象分配内存,并将对象引用保存在栈上或其他对象字段中

数组对象同样在堆中分配内存,多出了一个数字长度字段且在空间上是连续的,有利于快速访问。

2.连接

1.验证

目的:确保Class文件中的内容符合《Java虚拟机规范》,这些代码运行后不会危机虚拟机自身的安危。

1.文件格式验证

如是否以魔数开头,主次版本号是否被接收,指向常量池的索引值中是否会指向不存在的常量或不符合类型的常量。。。。

2.元数据验证

对字节码进行语义分析如这个类是否有父类除(Object),有无继承final修饰的类,非抽象类是否实现了父类或接口中要求实现的方法。。。

3.字节码验证

重要概念StackMapTable,JDK6后新增存在于Code属性中

代码语言:java
复制
StackMapTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_entries;
    stack_map_frame entries[number_of_entries];
}
stack_map_frame表示一个栈帧,它有多个变体,常见的栈帧类型包括:
	SameFrame:当前帧与前一个帧的局部变量表和操作数栈相同。
	SameLocals1StackItemFrame:局部变量表未变化,但操作数栈有一个新的项。
	ChopFrame:局部变量表缩短了一些项。
	AppendFrame:局部变量表增加了一些项。

它记录了方法执行时,在特定字节码指令的栈帧位置,以便JVM可以快速验证字节码的正确性。

验证原理:在字节码的执行过程中,虚拟机会根据字节码操作调整栈帧的状态,例如iload会从局部变量表加载一个int值并压入操作数栈;iadd操作会从操作数栈弹出两个int值并执行加法运算。

在这其中StackMapTable保证不会将int值当作float来操作,不能将null引用当作对象来操作。通过它,字节码验证器可以跳过详细的计算,直接从中获取栈帧信息,确保操作正确。大大优化了验证流程。

我们打破沙锅问到底。所以StackMapTable中的数据是从哪里来的?什么时候生成的?

其实猜也能猜出来,毕竟现在从一个java文件到真正运行起来也就执行了几步,距离真正运行还差很远。我们的编译器在生成字节码文件时,会进行控制流分析,确定代码执行路径。如if-else,循环,异常处理,方法调用。

1.基本块分割:把程序分为一段段不包含跳转的连续代码
2.计算栈状态:编译器跟踪每个基本块的入口栈帧状态。如进入某基本块后,记录操作数栈中有那些类型的值,局部变量表存储了那些类型的数据。
3.记录关键位置关键位置栈帧状态:将第二点计算出来的值保存。包括不限于方法调用入口点,异常处理开始位置,跳转指令的目标位置。这些东西就是StackMapStack中的Frame
4.符号引用验证

发生在虚拟机将符号引用转化为直接引用的时候。这个动作发生在解析连接的第三阶段——解析阶段。符号引用包括:类符号java/lang/Object,字段符号name:Ljava/lang/string;表示字段name类型时string,方法符号methodName(I)V表示一个方法methodName参数类型为Int,返回类型为Void

验证能否通过字符串描述的全限定名找到对应的类。在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段 。符号引用中的类,方法,字段的可访性(privaet/protect/public/<package>)是否可被当前类访问。

常见的验证失败:类不存在:符号引用指向的类在类路径中无法找到,导致ClassNotFoundException。字段不存在::符号引用中的字段名称或字段描述符不匹配,例如符号引用指向一个int类型的字段,而类中实际字段类型是String,则会抛NoSuchFieldError

方法不存在:符号引用中的方法名称或方法描述符不匹配,例如符号引用描述的是methodName(int)方法,但类中实际方法签名是methodName(String),则会抛出NoSuchMethodError

验证阶段对于虚拟机类加载机制来说非常重要,但却不是一个必须要执行的阶段。哈哈哈懵了吧。
因为这一验证阶段只有通过与不通过的差别,只要通过了验证,其后就对程序没有任何影响了。如果程序运行的代码已经被反复使用和验证过,那么在生产环境可以考虑使用-Xvertify:none参数来关闭此验证,缩短虚拟机类加载时间。

3.准备

将静态变量(static)分配内存并设置类变量初始值。设0,设null。如果被final修饰,在编译时就会直接赋值。

4.解析

将常量池中的符号引用转化为直接引用的过程。

除了动态invokedynamic指令外,其他的指令在进行符号进行解析请求时,保证如果一个符号引用已经被成功解析过来,那么后面的请求同样要成功,而如果失败了那其他解析请求同样失败。虚拟机会将第一次解析结果进行缓存。

1.类或接口的解析 2.字段解析 3.方法解析 4.接口方法解析

5.初始化

类加载的最后一个阶段。直接来说就是执行类构造器<clinit>方法()的过程。他是javac编译器自动生成的,收集了类中所有类变量的复制动作和静态语句。JVM保证子类的<clinit>执行器父类的<clinit>执行完毕。它不是必要的,如果一个类没有静态语句块和对变量的复制操作,可以不生成。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类加载过程
    • 1.加载
      • 额外讲一下数组与非数组对象在加载中的区别。
      • 内存分配的区别
    • 2.连接
      • 1.验证
    • 3.准备
    • 4.解析
    • 5.初始化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档