简单理解 Java 类加载过程

一个java文件从编码完成到最终执行,一般主要包括两个过程:编译和运行。编译,即是写好的Java文件通过javac命令编译成字节码,也就是常见的.class文件;运行,则是把编译.class文件交给Java虚拟机(JVM)运行。

类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。

例子:JVM 在执行某段代码时,遇到了 class A,然而此时内存中并没有 class A 的相关信息,于是 JVM 就会到相应的 class 文件中去寻找 class A 的类信息,并加载进内存中,这就是所说的类加载过程。

类加载过程分为 加载、验证、准备、解析、初始化、使用、卸载 7个阶段

1、加载

加载阶段主要是:

(1)、通过类的权限定名获取类的二进制文件流

(2)、将字节流所代表的静态存储结构转变为方法区的运行时数据结构

(3)、在堆中生成此类的java.lang.class对象,作为方法区这些数据的访问入口

加载阶段是开发可控性最强的阶段,比如说开发可以使用自定义类加载器去加载某个类,类的来源也可以是jar包、class文件、甚至是网络流。

重点:

(1)、字节码来源:一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译

(2)、类加载器。一般包括启动类加载器(BootstrapClassLoader)、扩展类加载器(ExtensionClassLoader)、应用类加载器(ApplicationClassLoader)、用户的自定义类加载器(CustomerClassLoader)。

注:为什么会有自定义类加载器?

(1)、由于 java 代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。

(2)、也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。

2、验证

验证是连接过程的第一步,它是为了确保class文件的字节流符合虚拟机的规范。

加载阶段加载class文件,并未规定class文件的来源,如果愿意,甚至可以手动编写class文件,但这样的class文件有可能不符合虚拟机规范,所以需要验证。它主要进行以下方面验证:

(1)、文件格式验证

(2)、元数据验证

(3)、字节码验证

(4)、符号引用验证

3、准备

准备阶段是为类变量正式分配内存并赋默认值的阶段,这些内存都在方法区内分配。

强调:只为类变量分配内存,即是为static变量分配内存,一般成员变量是在类被实例化时分配内存;只会为static变量赋默认值。假设有static变量:

public static int val = 123;

那么准备阶段,val的值会变成0,而不是123。而把val赋值为123的putstatic指令是在程序被编译后,存放在 clinit 方法中,所以val被赋值为123 发生在初始化阶段,即第5个阶段。

如果是final修饰的static变量呢?如果字段的字段属性表中存在ConstantValue属性,那么准备阶段,变量就会被初始化为ConstantValue存储的值。

假设上述变量为:

public static final int val = 123;

编译时javac会为val生成ConstantValue属性,准备阶段value的值就会成为123。

4、解析

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

(1)、符号引用是指字面量,能无歧义地定位到目标就好,它与虚拟机的内存布局无关。

(2)、直接引用是指可以直接访问到对象的指针或句柄,与内存布局相关的。

解析主要针对类或接口、字段、类方法、接口方法4类符号引用。

5、初始化

初始化是类加载过程的最后一步,除了加载过程,开发可以使用自定义类加载器参与外,其它过程全都是虚拟机自己控制着。初始化阶段,才真正开始执行Java程序代码。

准备阶段,为static变量分配内存并赋默认值,而初始化阶段则会执行clinit方法,为static变量分配代码中所指定的值。

(1)、<clinit>方法是虚拟机自动生成的,编译器收集类中类变量的赋值语句、static静态语句块合并产生<clinit>方法。收集的顺序是代码顺序

(2)、<clinit>方法和 init 方法不同,它不需要显示调用父类<clinit>方法,因为虚拟机保证在调用子类<clinit>方法前,父类<clinit>方法已经执行完毕。

(3)、因为父类的<clinit>方法先执行,所以父类静态语句块要优先于子类的静态语句块。

(4)、<clinit>方法并不是必须的,如果代码中没有定义静态变量或者静态语句块,则没有<clinit>方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值