JVM中类加载的过程

类的生命周期

====================================================================

在这里插入图片描述

加载

================================================================

"加载"是类加载过程中的一个阶段,在这个阶段虚拟机做了3件事

  1. 通过一个类的全限定名获取定义此类的二进制流

  2. 通过这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

  3. 在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类的各种数据访问的入口。

在这里插入图片描述

注意,加载阶段与连接阶段的部分内容是交叉进行,加载阶段尚未完成,连接阶段可能已经开始了,但总体的顺序还是先加载再连接。

验证

================================================================

验证阶段是连接阶段的第一步,这个阶段的目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求。不会危害虚拟机自身。验证的内容包含如下4个阶段

  1. 文件格式验证

验证字节流的格式是否符合class文件的规范及是否能被当前虚拟机处理。

a.是否已魔数0xCAFEBABE开头

b.主次版本号是否在当前虚拟机处理范围之内

c.常量池的常量中是否有不被支持的常量类型tag标志

d.指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量

e.CONSTANT_Utf8_Info型的常量是否有不符合UTF-8编码的数据

f.Class文件中各个部分及文件本身是否有被删除的或附加的其他信息

  1. 元数据验证

语言分析,保证描述信息符合java语言规范要做

a.这个类是否有父类

b.这个类的父类是否继承了不允许继承的类(final修饰)

c.非抽象类,是否实现了父类及接口中的所有的抽象方法

d.类中字段,方法是否和父类产生矛盾

  1. 字节码验证

本阶段是最复杂的阶段,通过数据流和控制流分析确定程序语义是否合法和符合逻辑。

  1. 符号引用验证

准备

================================================================

本阶段也称为零值阶段,也就是将类中的类变量分配内存及赋初值,此处的初值是赋予对应类型的零值,如下

public static int value=123;

那么变量value的值在这个阶段赋予的是0而不是123,这里int为0,long为0L,boolean为false… …真正的初始化赋值是在初始化阶段进行的。同时要注意如果类变量被final修饰那么准备结果的结果就会不同

public final static int value=123;

编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123.

解析

================================================================

解析阶段就是将常量池内的符号引用替换为直接引用的过程。解析阶段包含以下内容。

  1. 类或接口的解析

  2. 字段解析

  3. 类方法解析

  4. 接口方法解析

初始化

=================================================================

在准备阶段已经对类变量赋值过一次了,当时是赋予的零值,而到了初始阶段则会根据我们主观计划去初始化类变量和其他资源,其本质初始化阶段是执行类构造器方法的过程,在这个过程中有几个要注意的地方

  1. 静态语句块只能访问到定义在静态语句块之前的变量。可以给定义在之后的变量赋值但不可以访问

public class Test2 {

static{

i = 10; // 能赋值

System.out.println(i); // 但不能访问,提示 非法向前引用

}

static int i = 0;

}

  1. 不需要显示的调用父类的类构造器。虚拟机保证子类的方法执行之前父类的方法已经执行

  2. 由于父类先执行方法,所以父类的静态语句块会优先于子类的静态语句块执行

public class Test2 {

static class Parent{

static int A = 1;

static{

A = 2;

}

}

static class Sub extends Parent{

static int B = A;

}

public static void main(String[] args) {

System.out.println(Sub.B);

}

}

输出结果是2而不是1.

  1. 方法对于类或接口来说并不是必需的。如果一个类中没有静态语句块也没有对变量的赋值操作,那么编译器可以不为这个类生成方法。

  2. 接口中不能使用静态语句块,任然有变量赋值操作,所以接口和类一样也会生成()方法,但接口和类不同,接口中的()方法不需要先执行父接口的()方法,只有当父接口中定义的变量使用时父接口才会初始化。接口的实现类在初始化的时候一样不会执行方法

  3. 同一个类只会被加载一次,/()方法也只会执行一次,如果多线程环境中只会有一个线程执行方法,其他线程需要等待其执行完成。如果执行比较耗时那么会产生阻塞。

public class Test2 {

static {

if (true) {

System.out.println(Thread.currentThread().getName()+“开始初始化…”);

while (true) {

// 死循环 阻塞

}

}

}

}

/**

  • 测试

  • @author 波波烤鸭

  • @email dengpbs@163.com

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值