发表于: 2020-02-14 16:32:42
1 1104
今天完成的事情:
一个类从被加载到虚拟机内存中开始到卸载内存为止,它的整个生命周期会经历加载,验证,准备,解析,初始化,使用,卸载七个阶段,其中验证,准备,解析统称为连接。
1、类的生命周期
2、类的加载过程
2.1、加载
加载阶段是整个类加载过程中的一个阶段,在加载阶段,Java虚拟机需要完成以下3件事情。
1)通过一个类的全限定名来获取定义此类的二进制流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
2.2、验证
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合《JAVA虚拟机规范》的全部约束条件,保证这些信息被当成代码运行后不会危害虚拟机的自身安全。会有以下几种验证方式:
1)文件格式验证
第一阶段主要是验证字节流是否符合Class文件格式的规范,并且能被当前版本虚拟机处理。
比如:是否以魔数0xCAFEBABE开头,主次版本号是否在当前java虚拟机接受范围内等等。
2)元数据验证
第二阶段是对字节码描述的信息进行语义分析,以保证描述的信息符合《Java语言规范》
比如:这个类是否有父类(除了java.lang.Object所有的类都有父类)。这个类的父类是否继承了不允许被继承的类(被final修饰的类)
3)字节码验证
第三阶段是整个验证过程最复杂的一个阶段,主要是通过数据流的分析和控制流的分析,确定程序语义是否合法,符合逻辑。
对类的方法体进行验证分析,保证被验证的类方法在运行时不会危害虚拟机安全的行为。比如:
保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作。不会出现栈里放一个int数据类型,使用时却按long类型来加载本地变量。保证方法体的类型转换总是有效的。
4)符号引用验证
最后一个阶段的校验行为发生在虚拟机符号引用转换为直接引用的时候,这个转换动作在解析的阶段。可以看做是对类自身以外的各类信息的进行匹配性校验,看类是否缺少或者禁止访问它依赖的某些外部类,方法,字段等资源。比如:
符号引用中通过字段串描述的全限定名是否能找到对应发类。符号引用中的类,字段,方法的可访问性。是否能被当前类访问。
2.2.3准备
准备阶段是正式为类中定义的变量(静态变量,static修饰的变量)分配内存并设置初始值。
public static int num = 666;
此时在准备阶段过后的初始值为0而不是666;将num赋值为123的putstatic指令是程序被编译后,存放于类构造器<client>方法之中.
public static final int num= 666;
加上final关键字后,此时num的值在准备阶段就是666.
基本数据的零值
数据类型 零值 数据类型 零值
int 0 float 0.0f
long 0L double 0.0d
char \u0000 reference null
byte byte(0) short short(0)
boolean false
2.2.4解析
Java虚拟机把常量池内的符号引用转换为直接引用。
1)符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
2)直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在
主要有以下四种:
类或接口的解析
字段解析
类方法解析
接口方法解析
2.2.5初始化
类的初始化是类加载过程中的最后一步。
初始化阶段是执行类构造器<client>方法的过程。<client>方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证<client>方法执行之前,父类的<client>方法已经执行完毕。如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。
java中,对于初始化阶段,有且只有以下五种情况才会对要求类立刻“初始化”(加载,验证,准备,自然需要在此之前开始):
使用new关键字实例化对象、访问或者设置一个类的静态字段(被final修饰、编译器优化时已经放入常量池的例外)、调用类方法,都会初始化该静态字段或者静态方法所在的类。
初始化类的时候,如果其父类没有被初始化过,则要先触发其父类初始化。
使用java.lang.reflect包的方法进行反射调用的时候,如果类没有被初始化,则要先初始化。
虚拟机启动时,用户会先初始化要执行的主类(含有main)
jdk 1.7后,如果java.lang.invoke.MethodHandle的实例最后对应的解析结果是 REF_getStatic、REF_putStatic、REF_invokeStatic方法句柄,并且这个方法所在类没有初始化,则先初始化。
评论