目录
生命周期的概述:
加载阶段:
连接阶段:
1、验证:
2、准备:
3、解析
初始化阶段:
总结:
类的生命周期描述了一个类加载、使用、卸载的整个过程。
生命周期的概述:
类的生命周期:
可以被看做是5个或者7个步骤
- 加载
- 连接*
- 验证
- 准备
- 解析
- 初始化*
- 使用
- 卸载
加载阶段:
1、加载阶段的第一步是 类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息(将其加载到内存中)
- 渠道一:本地文件——磁盘上的字节码文件
- 渠道二:动态代理——程序运行时使用动态代理生成(spring)
- 渠道三:通过网络传输的类——小程序(被淘汰)
2、类加载器在加载完类之后,java虚拟机会将字节码中的信息保存到 方法区中。
3、类加载器在加载完类之后,java虚拟机会将字节码中的信息保存到内存的方法区中。 方法区中会生成一个 InstanceKlass对象 ,保存类的所有信息,里面还包含实现特定功能比如多态的信息。
4、同时,Java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.Class对象。 作用是在Java代码中去获取类的信息以及存储 静态字段 的数据。

方法区 和 堆区 的信息相互关联,可以通过一个去调用另一个。
- 对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中的所有信息,这样java虚拟机可以很好的来控制开发者访问数据的范围。
注:方法区中的instanceKlass使用c++编译的,java不太好操作,所以在堆中创建了堆区中通过java语言包装后的对象(再从方法区中拷贝的过程中会将一些内容剔除掉,只包含我们需要的。是为了安全性的考虑)
全部加载到了内存中
连接阶段:
- 验证:验证是否满足java虚拟机规范
- 准备:给静态变量赋初值
- 解析:将常量池中的符号引用替换成指向内存的直接引用
1、验证:
连接阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规范》中的约束。这个阶段不用程序员参与。
主要有四部分:
- 魔数的校验,文件格式的校验
- 元信息验证,例如类必须有父类
- 验证程序执行指令的语义,比如方法内的指令执行到一半强制跳转到其他方法中去
- 符号引用类型,例如是否访问了其它类中private的方法等。

2、准备:
准备阶段为 静态变量分配内存并 设置初始值。
int | 0 |
long | 0L |
short | 0 |
char | ‘\u0000’ |
byte | 0 |
boolean | false |
double | 0.0 |
引用 | null |
定义的都是静态变量
当我定义数据时使用了final关键字,这就代表了在以后这个数据的值不再会发生变化了,所以就会在准备阶段直接将代码中的值进行赋值。他就会直接在堆区中的java.lang.Class中进行赋值了。
3、解析
- 解析阶段主要是将常量池中的符号引用替换为直接引用。
- 符号引用就是在字节码文件中使用编号来访问常量池中的内容。
- 直接引用不再使用编号,而是使用内存中地址进行访问具体的数据。(性能行对较高)
我们使用jclasslib查看时可以看到使用编号号引用的信息,我们使用HSDB就可以查看到实际上使用的是内存地址的信息。
初始化阶段:
- 初始化阶段会执行静态代码块中的代码,并为静态变量赋值。
- 其实就是 会执行字节码文件中clinit的字节码指令。【赋的值是从操作数栈中取出来的】

操作数栈:一种数据结构,用来存放参与运算的数据的临时存储区域。


clinit方法中的执行顺序与java中编写的顺序是一样的。
- 以下的几种方式会导致类的初始化:
1、访问一个类的静态变量或者静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化。
2、调用Class.forName(String className)
这个方法中有两个重载方法,第一个是只传递一个参数 会默认进行初始化,第二个是可以指定是否进行初始化。
3、new一个该类的对象时。
4、执行Main方法的当前类。

- clinit指令在特定情况下不会出现,比如:如下几种情况是不会进行初始化指令执行的。
1、无静态代码块且无静态变量赋值语句。
2、有静态变量的声名,但是没有赋值语句。
3、静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化。
父类——子类:
- 直接访问父类的静态变量,不会触发子类的初始化。
- 子类的初始化clinit调用之前,会先调用父类的clinit的初始化方法。


数组的创建不会导致数组中元素的类进行初始化。
final修饰的变量如果赋值的内容需要执行指令才能得出结论,会执行clinit方法进行初始化。
总结:
- 加载:根据类的全限定名吧字节码文件的内容加载并转换成合适的数据放入内存中,存放在方法区和堆上
- 连接:
- 验证:魔数、版本号等验证,一般不需要程序员关注
- 准备:为静态变量分配内存并设置初始值
- 解析:将常量池中的符号引用(编号)替换成直接引用(内存地址)
- 初始化:执行静态代码块和静态变量的赋值
- 金泰变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化(除非要执行方法)
- 直接访问父类的静态变量,不会触发子类的初始化。子类的初始化cinit调用之前,会先调用父类的cinit初始化方法。