jvm加载字节码过程

本文详细介绍了JVM加载字节码的过程,包括加载、验证、准备、解析和初始化五个步骤。加载阶段,字节码从不同来源获取并转化为内存中的数据结构;验证阶段确保字节码的正确性和安全性;准备阶段为类变量分配内存并赋予默认值;解析阶段将符号引用替换为直接引用;初始化阶段执行类构造器。类加载器在加载过程中起关键作用,遵循双亲委派机制,保证核心类库的安全性。

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

前面提到,java代码编译成字节码后,由jvm加载并执行,那么jvm加载的过程是由类加载子系统来执行的。java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最后形成可以被虚拟机直接使用的java类型,这个过程被称为虚拟机的类加载机制。

加载过程

类从被加载到内存到卸出内存,分为以下几个过程

  • 加载
  • 连接
    • 验证
    • 准备
    • 解析
  • 初始化
  • 使用
  • 卸载
    在这里插入图片描述
1.加载

这里的加载指的是加载过程中把字节码从文件中的结构加载到内存中的数据结构的一步,而不是完整的加载过程。这个是运行的第一步。jvm的加载时机并没有明确约束,所以一般都是各个虚拟机来实现。目前jvm可以从各个途径来获取字节码文件。加载阶段,jvm会做以下处理:
1.通过一个类的全限定名来获取该类的二进制字节流。
2.将这个字节流的所代表的静态结构转化称方法区的运行时数据结构。
3.在内存中生存一个代表这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口。

由于这里没有明显的约束,所以灵活性较大,比如从哪里获取二进制字节流就有很多途径。

  • 从zip压缩包获取,jar,war,ear等都是这一类
  • 网络传输获取,早期的Web Applet
  • 运行过程中生产,java的动态代理技术
  • 数据库获取
  • 加密文件获取,通过自定义类加载器来解密文件生成字节码,保护程序。
2.验证

把字节码加载到内存后,就进入到了连接阶段,而连接过程首先是对字节流进行验证,确保符合java虚拟机规范,来保证字节码格式正确或者排除部分恶意攻击意图。
验证过程主要有以下四个动作。

  • 文件格式验证
    字节码文件都是以0xCAFEBABE开头,主次版本后需要在当前虚拟机接受范围…
  • 元数据验证
    语义分析,是否有父类,类是否允许被继承,抽象类实现类是否实现相关方法…
  • 字节码验证
    比较复杂的一个阶段,会类的方法体逻辑进行验证,例如类型转换是否有效,字节码指令跳转异常等。
  • 符号引用验证
    验证符号引用等否找到对应类,类权限是否能被引用等。
3.准备

准备过程是为类变量(静态变量)分配内存和初始化值的阶段.java8后这些类变量和Class对象都是放在堆中了。这里初始化变量值是数据类型的零值,不是实际值,此外这里还没有创建实例变量,所以仅仅是对类变量进行内存的分配。例如:

private static int value = 10;

value值在这个阶段的数值化是0而不是10。但是如果时final修饰的静态变量,则在准备阶段直接赋值为10.

4.解析

解析阶段时将java虚拟机中变量池中的符号引用替换为直接引用的过程。简单来说就是把类名限定转换为实际内存中的Class对象引用。

5.初始化

初始化阶段就是执行类构造器()方法的过程,这里的是java编译器生成在字节码中的,它会对类变量进行实际赋值和执行静态语句块。如果代码中没有静态变量和静态语句块,那个就没有这个类构造器。

类加载器

java类加载过程的加载阶段是通过类加载器来加载的,这里为程序提供了很大的灵活且。类加载器从层级来分分为以下:

  • 引导类加载器
    c++编写的,加载<JAVA_HOME>\lib中的目录,如rt.jar,tools.jar
  • 扩展类加载器
    加载<JAVA_HOME>\ext目录下的jar包文件,提供扩展能力
  • 应用类加载器
    系统默认加载写自己写的代码编译后的的字节码文件。
  • 自定义类加载器
    由我们自定义的加载器,加载字节码,比如加载加壳后的文件。
双亲委派机制

类加载器的层级关系如下,他们是父子层级关系,但是并不是继承来实现的,因为启动类加载器是由c++来写的,而其他类加载器都是只是继承于java.lang.ClassLoader的,这里是通过组合来实现的逻辑关系。
在这里插入图片描述

而类加载是由哪一个类加载器加载的机制也称为双亲委派机制:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派到父类加载器来完成,每一个层次都是如此,因此最后的加载请求都是传送到最顶层的启动类加载器,只有当父类加载无法加载这个类时(这个类不是自己的加载范围),子加载器才会尝试自己去完成加载。
双亲委派机制非常重要,例如我们的代码中新建了一个java.lang包,包里面有一个String类,最后加载的都是java类库中的String类,因为双亲委派机制的原理,实际这个类时由启动类加载器去加载javaHome的String类。这样保证了核心类库不会被篡改从而影响系统性能和安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值