标签:自定义 默认 code system weight 开始 order any 验证过
在类的加载阶段虚拟机需要完成下列三件事情:
对于非数组类的加载阶段(主要指加载阶段中获取类的二进制字节流的动作),可以使用系统提供的引导类加载器完成,也可以通过用户自定义的类加载器来完成(即重写一个类加载器的loadClass()方法)。
而对于数组类来说,数组类本身不通过类加载器创建,它由Java虚拟机直接创建。但数组类与类加载器仍然有很密切的关系,因为数组类的元素类型(指数组去掉所有维度的类型)最终还是要靠类加载器去创建。
数组类的创建规则遵循以下规则:
加载完成后,虚拟机外部的二进制字节流文件按照虚拟机所需的格式存储在方法区之中(方法区中的数据储存格式由虚拟机自行定义)。然后在内存中实例化一个java.lang.Class类的对象,(对于Hotspot虚拟机来说Class对象比较特殊,它虽然是对象,但是存放在方法区里面)这个对象将作为程序访问方法区中这些类型数据的外部接口。
验证时连接阶段的第一步,目的是确保Class字节流中包括的信息符合当前虚拟机的要求,并不会危害虚拟机自身的安全。
大致会完成下面几个阶段的验证:
第一阶段要验证字节流是否符合Class文件格式规范,并能被当前版本的虚拟机处理。比如:
小结:
由此可以看出加载阶段与连接阶段的部分内容有些是交叉进行的(如:加载阶段与文件格式验证动作),但是加载与连接这二个阶段仍保持着固定的先后顺序。
这个阶段是基于二进制字节流进行的,只有通过这个阶段的验证,字节流才会进入内存中的方法区进行储存,所以后面的验证阶段全部是基于方法区的储存结构进行的,不会再字节操作字节流。
第二个阶段主要是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。可能验证的内容有:
第三个阶段验证主要是通过数据流和控制流分析、确定程序语义是合法的、符合逻辑的。这个阶段将对类的方法体进行校验分析,保证被校验的类的方法在运行时不会危害虚拟机。
如果一个类方法体的字节码没有通过字节码验证,则肯定是有问题的;但是如果一个方法体通过了字节码验证,也不能说明其一定是安全的。
最后一个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候,这个动作将发生在连接的第三阶段——解析阶段,用于确保解析动作能正常执行。
符号引用可以看做是对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验,通常需要检验的有:
小结:对于虚拟机来说验证阶段是一个重要但非必要的阶段。比如第三方包中的代码已经被反复使用和验证过,则在实施阶段可以使用 -Xverify:none参数来关闭大部分类验证措施,节约虚拟机类加载时间。
准备阶段是正式为类变量(static修饰的变量)分配内存并设置类变量初始值的阶段,这些变量使用的内存都将在方法区中进行内存分配。
private static int value = 123;
设置类变量初始值通常是指数据的零值,如上述value在准备阶段过后的初始值是0。value赋值为123实在初始化阶段才会执行。
对于某些类字段的字段属性表中存在ConstantValue属性,那么在准备阶段字段将被初始化为ConstantValue所指定的值。例如:
private static final int value = 123;
此时value的值再准备阶段就初始化为123。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
对同一个符号引用进行多次解析请求是很常见的事情,除invokedynamic指令以外,虚拟机实现可以将第一次解析的结果进行缓存从而避免解析动作重复进行。
而对于invokedynamic指令不必须等到程序实际运行这条指令的时候,解析动作才能进行。相对的,其他的指令可以在刚刚完成加载阶段,还没有开始执行代码时就进行解析。
当前类为D,把一个从未解析过的符号引用N解析为一个类或接口C的直接引用。
解析一个未被解析过的字段引用
对字段所属的类或接口的符号引用进行解析
,解析成功则将这个类或接口用C表示。若C本身包含了简单名称和字段描述符与目标相匹配的字段,则直接返回该字段的直接引用,查找结束。搜索各个接口和它的父借口
,若果接口中包含了简单名称和字段描述符与目标相匹配的字段,则直接返回该字段的直接引用,查找结束。按照继承关系从下往上递归搜索其父类
,若在父类中包含了简单名称和字段描述符与目标相匹配的字段,则直接返回该字段的直接引用,查找结束。对该字段进行权限验证
,若发现没有访问权限,抛出java.lang.IlleagalAccessError异常。。解析类方法对应的类或接口的符号引用
,解析成功用C表示这个类。类C中查找
是否存在简单名称及描述符与目标相匹配的方法,有则返回直接引用,结束。类C的父类中查找
是否存在简单名称及描述符与目标相匹配的方法,有则返回直接引用,结束。类C的接口及其父类接口查找
是否存在简单名称及描述符与目标相匹配的方法,若有,则说明类C是一个抽象类,查找结束抛出异常。行权限验证
。在接口C中查找
是否存在简单名称及描述符与目标相匹配的方法,有则返回直接引用,结束。在接口C的父接口中查找
是否存在简单名称及描述符与目标相匹配的方法,有则返回直接引用,结束。注意:因为接口中的方法都是public的所以不需要进行权限验证。
类初始化是类加载的最后一步,此时才开始真正执行Java代码(字节码)。
初始化也可看成执行类构造器< clinit >()方法的过程。(注意与构造函数< init >()方法的区别)它有以下一些特点:
{
static{
i=0;
System.out.println(i);//这句会报错,不能再定义之前去访问
}
static int i = 1;
}
通过一个类的全限定名来获取来获取描述此类的二进制字节流,这个动作的代码模块称为“类加载器”。(在Java虚拟机外部实现)
标签:自定义 默认 code system weight 开始 order any 验证过
原文地址:http://www.cnblogs.com/0427mybirthday/p/7498793.html