首页 > 代码库 > class文件加载过程详解
class文件加载过程详解
java类的加载过程。
参考书籍:深入理解JAVA虚拟机
书中错误的地方,p222.
字段解析,在解析一个未解析过的字段时,书中说的是先解析字段表的class_index,
但是,字段表示没有class_index,
跟在字节码指令后边的字段的符号引用是CONSTANT_Field_info。
先对其中的class_index解析。
这个过程,比较复杂,要理解这个过程,你要对Class文件结构比较了解,尤其是字段表。
1.累加载的整体流程
加载--验证--准备--解析--初始化--使用--卸载
(注意,不是说非得等一个阶段完成才进行下一个阶段,其中有些是交叉的,比如解析,,有可能在使用的时候还会有解析)
一、加载
(1)通过一个类的全限定名找到这个类二进制流
(2)把这个Class文件的字节流转换成方法区的运行时数据结构(这个我们有理解好)
(3)在内存中生成一个代表这个类的Class对象(java hotspot 虚拟机中,这个对象在方法区),作为方法区的入口。
二、验证
(1)文件格式验证 验证这个字节流是不是符合Class文件格式的规范
(2)元数据验证 这个类是不是有父类(除了Object,所有的类都要有父类)
这个类是不是继承了final
是不是实现了继承的接口,抽象类的方法
类中的字段,方法是不是与父类矛盾(例如,覆盖了父类final方法,交叉覆盖,不符合规定的重载等)
。。。。。。
一句话,我觉得就是看是不是符合java的元数据语法(类的继承,方法的覆盖,重载,)
(3)字节码验证 就是对java方法中的语义是不是可发的、符合逻辑的。
(4) 符号引用验证 发生在虚拟机将符号引用转化为直接引用的时候。
三、准备
在这个阶段,会为类变量分配内存,并且设置类变量的初始值(默认值)
实例变量会在对象实例化是分配在堆中(也会有一个设置零值的过程)
特殊情况,如果类字段有一个ConstantValue属性,会直接初始化为这个属性指定的值
关于ConstantValue见我对于字段表的分析。
四、解析
jvm关于解析是这样的,要求在执行 用于操作符号引用的 字节码指令之前,对符号引用进行解析。
就是解析字节码后操作的符号引用(在常量池中)。
什么是解析?
就是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用:以一组符号来描述所引用的的目标,与内存布局无关,比如,一个类的符号引用就是它的全限定名,
在常量池中表示为:CONSTANT_Class_info。(参考我对常量池的分析)
直接引用:可以使直接指向目标的指针、相对偏移量或是一个句柄。和虚拟机实现的内存布局相关
类或者接口的解析:
假设当前代码所处的类为D,如果要把一个符号引用解析为类或者接口C的直接引用:
(1)如果C不是数组,就把C的全限定名传给D的类加载器区加载。
(2)如果是数组类型,先有D的类加载器去加载数组类型。然后由虚拟机生成一个代表此数组维度和元素的数组对象
(3)进行验证,确认D是否对C有访问权限。
字段的解析:
要解析一个字段,首先对这个符号引用中的class_index进行解析。也就是字段所属的类或接口的符号引用。将这个类或接口记为C。
(1)如果C中本身就包含了简单名字和字段描述符都与目标相匹配的字段(这个时候查的是字段表),就返回这个字段的直接引用。
(2)否则,如果C中实现了接口,就往接口中找。
(3)否则,就往父类里边找。
(类和接口)方法的解析:
类似于字段。
五、初始化
就是调用<clinit>,<clinit>是编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并生成的。
<clinit>不是必须的。
<clinit>与<init>不同,它不需要显示调用父类构造器,会保证在子类的<clinit>之前,父类的已经执行过了。
而在<init>中,会先调用父类的<init>,然后是赋值动作、初始化快(这俩按照出现的顺序)、构造方法中的语句
可以看字节码来对比
本文出自 “厚积薄发” 博客,请务必保留此出处http://duanzhenyue.blog.51cto.com/9360091/1550118
class文件加载过程详解