虚拟机类加载机制 (2)

符号引用验证的目的是确保解析动作能正常执行,如果无法通过符号引用验证,将会抛出一个java.lang.IncompatibleClassChangeError异常的子类,如java.lang.IllegalAccessError、java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。
验证阶段对于虚拟机的类加载机制来说,是一个非常重要、但不一定是必要的阶段。如果所运行的全部代码都已经被反复使用和验证过,在实施阶段就可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

 

2.2、准备
准备阶段是正式为类变量分配内存并设置类变量初始值得阶段,这些内存都将在方法区中进行分配。
这个阶段中有两个容易产生混淆的概念需要强调一下,首先是这时候进行内存分配的仅包括类变量(被static修饰的变量),
而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其次是这里所说的初始值“通常情况下”是数据类型的零值。

例如

public static int v=1;
在准备阶段中,v会被设置为0
在初始化的<clinit>中才会被设置为1
对于static final类型,在准备阶段就会被赋上正确的值
public static final int v=1;

 

2.3 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用发的过程。

符号引用(Symbolic References): 符号引用以一组符号来描述所引用的目标,符号可以使任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用于虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中。

直接引用(DirectReferences):直接引用可以是直接指向目标的指针,相对偏移量或是一个能简介定位到目标的句柄,直接引用是与虚拟机实现的内存布局相关的,
                                               统一符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同,如果有了直接引用,那引用的目标必定已经在内存中存在。


虚拟机规范之中并未规定解析阶段发生的具体时间,只要求在执行anewarray、checkcast、getfield、getstatic、instanceof、invokeinterface、invokespecial、
invokestatic、invokevirtual、multianewarray、new、putfield和putstatic这13个用于操作符引用的字节码指令之前,先对它们使用的符号引用进行解析。
解析动作主要针对类或接口、字段、类方法、接口方法四类符号引用进行,分别对应于常量池的CONSTANT_Class_info,CONSTANT_Fieldref_info、CONSTANT_Methodref_info以及CONSTANT_InterfaceMethodref_info 四种常量类型。

 

2.3.1类或接口的解析

假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,那虚拟机完成整个解析的过程需要包括以下步骤:
1)如果C不是一个数组类型,那虚拟机将会把代表N的全限定名传递给D的类加载器去加载这个类C。在加载过程中,由于无数据验证、字节码验证的需要,又将可能触发其他相关类的加载动作,例如加载这个类的父类或实现的接口。

       一旦这个加载过程出现了任何异常,解析过程就将宣告失败。
2)如果C是一个数组类型,并且数组的元素类型为对象,也就是N的描述符会是类似“Ljava.lang.Integer”的形式,那将会按照第一点的规则加载数组元素类型。如果N的描述符如全面所假设的形式,

      需要加载的元素类型就是“java.lang.Integer”,接着由虚拟机生成一个代表次数组维度和元素的数组对象。

2.3.2.字段解析
要解析一个未被解析过的字段符号引用,首先对堆字段表内class_index项中索引CONSTANT_Class_info符号引用进行解析,也就是字段所属的类或接口的符号引用。

如果在解析这个类或接口符号引用的过程中出现了任何异常,都会导致字段符号引用解析的失败。如果解析成功完成,那将这个字段所属的类或接口用C表示,虚拟机规范要求按照如下步骤对C进行后续字段的搜索:
1)如果C本身就包含了简单名称和字段描述符都与目标匹配的字段,则返回这个字段的直接引用,查找结束。
2)否则,如果在C中实现了接口,将会按照继承关系从上往下递归搜索各个接口和它的父接口。如果接口中包含了简单名称和字段描述符都与目标匹配的字段,则返回这个字段的直接引用,查找结束。
3)否则,如果C不是java.lang.Object的话,将会按照继承关系从上往下递归搜索其父类,如果接口中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
4)否则,查找失败,抛出java.lang.NoSuchFieldError异常。
如果查找过程成功返回了引用,将会对这个字段进行权限验证,如果发现不具备对字段的访问权限,将抛出java.lang.IllegalAccessError异常。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdjjf.html