JVM类加载机制
阅读数:76 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
从类被加载到虚拟机内存中开始,到卸载出内存为止,类的生命周期包括:
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。
加载、验证、准备、初始化、卸载这5个阶段的顺序是固定的,解析阶段则不一定,可能在初始化阶段结束后开始。
二、加载
1、开始时间:
虚拟机规范并没有强制约束什么时候开始加载,而是交由虚拟机的具体实现自由把握,但有4种情况必须立即对类进行初始化,所以要立即加载。
2、任务:
(1)通过一个类的全限定名获取定义这个类的二进制流
虚拟机规范并没有指明二进制流从哪一个Class文件中获取(完全可以通过自定义类加载器改变)。
(2)将这个字节流代表的静态存储结构转换为方法区的运行时数据结构
(3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法的访问入口。
三、验证
1、任务:
Java语言本身是相对安全的语言(相对C/C++),使用纯粹的Java代码无法做到诸如访问数组边界之外的数据、将一个对象转型为它未实现的数据类型、跳转到不存在代码行等,如果这样做,编译器将拒绝编译,但是Class文件不一定由Java源码编译而来,如用十六进制直接编写来产生Class文件。
虚拟机规范对该阶段仅要求如果验证到输入的字节流不符合Class文件的存储格式,就抛出一个java.lang.VerifyError异常,具体检查哪些方面、如何检查、何时检查,都未做强制要求或明确说明。
(1)文件格式验证
是否以魔数0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内等
该阶段的验证是基于字节流的,经过这个阶段的验证后,字节流才会进入内存的方法区中进行存储,故后面的三个验证阶段是基于方法区的存储结构进行的
(2)元数据验证
对字节码描述的信息进行语义分析。
(3)字节码验证
进行数据流和控制流分析,jdk1.6之后的javac编译器进行一项优化,给方法体和Code属性表中增加一个新属性:StackMapTable,该属性保存了方法体中的类型信息,可以使字节码验证时的类型推导变为类型检查而节省时间。在JDK1.6HotSpot虚拟机中提供了-XX:-UseSplitVerifier选项来关闭这项优化,或者使用-XX:+FailOverToOldVerifier选项在类型检查失败时退回到使用类型推导方式进行检验。
(4)符号引用验证
对类自身以外的信息进行匹配校验,该校验发生于虚拟机将符号引用转化为直接引用的时候。
如果运行的全部代码都已经被反复使用和验证过,在实施阶段可以使用-Xverify:none参数来关闭大部分的类验证措施以缩短虚拟机类加载的时间
四、准备
1、任务:
虚拟机在准备阶段为类变量(static)分配内存,并设置类变量初始值,这些内存都将在方法区分配。
五、解析
1、开始时间:
虚拟机规范并未规定解析动作发生的具体时间,仅要求在执行anewarray、checkcast、getfield、getstatic、instanceof、invokeinterface、invokespecial、invokestatic、invokevirtual、multianewarray、new、putfield和putstatic这13个用于操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析。
六、初始化
1、开始时间:
有具只有4种情况必须立即对类进行初始化:
(1)遇到new (使用new关键字实例化对象)、getstatic(获取一个类的静态字段,final修饰符修饰的静态字段除外)、putstatic(设置一个类的静态字段,final修饰符修饰的静态字段除外)和invokestatic(调用一类的表态方法)这4条字节码指令时,如果类还没有初始化,则必须首先对其初始化
(2)使用java.lang.reflect包中的方法对类进行反射调用时,如果类还没有初始化,则必须首先对其初始化。
(3)当初始化一个类时,如果其父类还没有初始化,则必须首先初始化其父类
(4)当虚拟机启动时,需要指定一个主类(main方法所在的类),虚拟机会首选初始化这个主类。
2、任务:
直到初始化阶段,才真正开始执行类中定义的Java程序代码。简单说,初始化阶段即虚拟机执行类构造器<clinit>()方法的过程。
最后欢迎大家访问我的个人网站:1024s