JVM的类加载过程

本文详细介绍了JVM的类加载阶段,包括加载、验证、准备、解析和初始化。在准备阶段,静态变量会被赋予默认值,初始化阶段才会执行用户设定的值。解析过程将符号引用替换为直接引用。文章还提到了不触发类初始化的几种情况,并穿插了面试题和美文佳句。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每日一句

人到情多情转薄,而今真个不多情。

每日一句

The frog in the well knows nothing of the great ocean. 井底之蛙,不知大海。

JVM 的类加载阶段

JVM 的类加载分为五个阶段:

1. 加载:被虚拟机读入内存

2. 验证:验证 Class 字节流的数据是否遵守JVM的规定

3. 准备:正式为类变量(静态变量)分配内存并设置初始值,并非代码中设置的值

4. 解析:将常量池中的符号引用解析为直接引用

5. 初始化:真正执行类中定义的java代码

加载

指 JVM 读取 class 文件,并且根据 Class 文件描述创建 java.lang.Class 对象的过程。

类加载过程主要包含将 Class 文件读取到运行时区域的方法区内,在堆中创建 java.lang.Class 对象,并封装类在方法区的数据结构的过程。

在读取 Class 文件是既可以通过文件的形式读取,也可以通过 jar 包、war 包读取,还可以通过代理自动生成 Class或其他方式读取

验证

主要用于确保 Class 文件符合当前虚拟机的要求,保障虚拟机自身的安全,只有通过验证的 CLass 文件才能被 JVM 加载

准备

主要工作是在方法区中为类变量分配内存空间并设置类中变量的初始值。 初始值指不同数据类型的默认值,这里需要注意 final 类型的变量和非final类型的变量在准备阶段的数据初始化过程不同

例如一个成员变量定义如下: public static long value = 1000; 在以上代码中,静态变量 value 在准备阶段的初始值是0,将 value 设置为 1000 的动作是在对象初始化时完成的,因为 JVM 在编译阶段会将静态变量的初始化操作定义在构造器中。 public static final int value = 1000;

则JVM在编译阶段后会为final类型的变量value生成其对应的ConstantValue属性,虚拟机在准备阶段会根据ConstantValue属性将value赋值为1000。

总结:静态变量会赋两次初值,准备阶段赋零值,初始化时赋用户设定值,而final变量会在准备阶段一次性赋值完毕

解析

JVM 会将常量池中的符号引用替换为直接引用。

初始化

主要通过执行类构造器的  方法为类进行初始化。  方法是在编译阶段由编译器自动收集类中静态语句和变量的赋值操作组成的。JVM规定,只有在父类的  方法都执行成功后,子类中的  方法才可以被执行。 在一个类中既没有静态变量赋值操作也没有静态语句块时,编译器不会为该类生成  方法。

在发生以下几种情况时,JVM不会执行类的初始化流程:

1. 常量在编译时会将其常量值存入使用该常量的类的常量池中,该过程不需要调用常量所在的类,因此,不会触发该常量类的初始化。

2. 在子类引用父类的静态字段时,不会触发子类的初始化,只会触发父类的初始化。

3. 定义对象数组,不会触发父类的初始化

4. 在使用类名获取 Class 对象时不会触发类的初始化

5. 在使用 Class.forName 加载指定的类时,可以通过 initialize 参数设置是否需要对类进行初始化

6. 在使用ClassLoader默认的loadClass方法加载类时不会触发该类的初始化。

美文佳句

很多时候,事情的困境,常常是因为我们自己钻了牛角尖,此时,你需要做的就是改变。

完美主义者可以放下执念,允许自己有普通人都会犯的小迷糊;职场妈妈可以直面现实,一个人永远做不到家庭和事业的双百分;承担了过多工作任务的员工,可以尝试向上级反映,寻求资源或调整目标……这些,都是我们应当并可以作出的改变。

正如这句话所说:世界上从来都没有所谓的奇迹,命运一直都掌握在我们自己手里。想要改变自己的命运,最重要的就是改变自己。当你开始改变自己的时候,很多东西就跟着改变了。

下一次,当烦恼降临时,不妨试试从自身找找问题。调整努力的方向和节奏,学会给心灵松绑,你会发现:很多事,其实没什么大不了。

面试题

SpringMVC 框架有什么用?

Spring Web MVC 框架提供”模型-视图-控制器”( Model-View-Controller )架构和随时可用的组件,用于开发灵活且松散耦合的 Web 应用程序。 MVC 模式有助于分离应用程序的不同方面,如输入逻辑,业务逻辑和 UI 逻辑,同时在所有这些元素之间提供松散耦合。

@RestController 和 @Controller 有什么区别?

@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 "ACCEPT" 请求头来决定。

SpringMVC工作原理?

1. 客户端发送请求到前端控制器 DispatcherServlet
2. DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器,请求获取 handler
3. 处理器映射器根据 url 找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
5. HandlerAdapter 经过适配器调用 具体处理器(Handler,也叫后端控制器)
6. Handler 执行完成返回 ModelAndView
7. HandlerAdaper 将 Handler 执行结果 ModelAndView 返回给 DispatcherServlet
8. DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器 进行解析
9. ViewResolver 解析后返回具体 View
10. DispatcherServlet 对 view 进行 渲染视图(即将模型数据填充至视图中)
11. DispatcherServlet 响应用户

你好,我是yltrcc,日常分享技术点滴,欢迎关注我:ylcoder

### JVM类加载机制及过程详解 JVM类加载机制是一个复杂但至关重要的过程,它决定了Java程序如何将`.class`文件中的二进制字节流转化为可以在JVM上执行的对象。以下是关于这一机制及其各个阶段的具体说明。 #### 1. 类加载器体系结构 JVM采用了一种层次化的类加载器模型,主要包括以下三种型的类加载器[^2]: - **启动类加载器 (Bootstrap ClassLoader)** 负责加载核心Java库(如`rt.jar`),通常位于`$JAVA_HOME/lib/`目录下。它是JVM的一部分,由本地代码实现,并不继承自`java.lang.ClassLoader`。 - **扩展类加载器 (Extension ClassLoader)** 负责加载标准扩展库,默认路径为`$JAVA_HOME/lib/ext/`或由系统属性`java.ext.dirs`指定的位置。它是`java.lang.ClassLoader`的一个子。 - **应用程序类加载器 (Application ClassLoader)** 又称为系统类加载器,用于加载用户的应用程序代码,默认路径为当前路径(即环境变量`CLASSPATH`)所指向的目录或jar包。 可以通过如下代码验证不同类加载器的作用范围[^3]: ```java public class TestJDKClassLoader { public static void main(String[] args) { // 输出核心加载器(null表示由启动类加载加载) System.out.println("引导类加载器:" + String.class.getClassLoader()); // 输出扩展加载器 System.out.println("扩展类加载器:" + DESKeyFactory.class.getClassLoader()); // 输出应用加载器 System.out.println("应用程序类加载器:" + TestJDKClassLoader.class.getClassLoader()); } } ``` --- #### 2. 类加载过程 类加载分为五个主要阶段:**加载、连接、初始化**。其中,“连接”又细分为三个子阶段:**验证、准备、解析**[^4]。 ##### (1)加载 在此阶段,JVM完成以下操作: - 根据的全限定名找到对应的`.class`文件; - 将其读取为二进制字节流并存储到内存的方法区中; - 创建一个代表此的`Class`对象实例,作为访问方法区数据的入口。 ##### (2)连接 ###### a. 验证 确保加载符合JVM规范,防止恶意代码破坏运行时安全。验证的内容包括但不限于: - 文件格式是否正确; - 字节码指令是否有非法跳转; - 访问权限是否合法等。 ###### b. 准备 为的静态字段分配内存空间,并设置默认初始值(注意此时不会执行任何代码逻辑)。例如对于`static int value = 10;`,在准备阶段只会将其赋值为`0`,而不是`10`。 ###### c. 解析 将中的符号引用替换为直接引用。这一步骤涉及对方法、字段以及父的查找和绑定。 ##### (3)初始化 这是最后一个阶段,在这里才会真正执行中的代码逻辑,主要是为了给的静态变量赋予正确的初值,并执行静态代码块。如果存在父,则需先完成父的初始化再处理子。 --- #### 3. 触发类加载的情况 并非所有的都会立即被加载至内存,只有满足特定条件时才触发加载行为: - 当主函数所在的被执行时; - 使用`new`关键字创建对象实例; - 获取或修改某个的静态成员变量; - 调用某的静态方法; - 利用反射API动态加载; - 初始化过程中发现尚未加载的父或接口。 --- #### 4. 双亲委派机制与例外情况 双亲委派机制规定了类加载器的工作顺序——当接收到一个类加载请求时,优先交由上级类加载器尝试加载;仅当下级无法完成加载任务时,本层加载器才会介入工作[^5]。然而某些框架(如Tomcat)出于隔离需求可能会打破这种模式,自行定制专属的类加载策略。 --- ### 总结 综上所述,JVM类加载机制不仅涵盖了多种型的加载器协作分工,还包含了多个严谨有序的操作环节以保障程序稳定高效地运行于虚拟环境中。理解这些原理有助于开发者更好地优化性能、排查错误甚至设计插件化架构解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值