准备
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初 始值的阶段,从概念上讲,这些变量所使用的内存都应当在方法区中进行分配,但必须注意到方法区 本身是一个逻辑上的区域,在JDK 8及之后,类变量则会随着Class对象一起存放在Java堆中,这时候“类变量在 方法区”就完全是一种对逻辑概念的表述了。
这时候进行内存分配的 仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其 次是这里所说的初始值“通常情况”下是数据类型的零值。
变量value在准备阶段过后的初始值为0而不是123,因为这时尚未开始执行任何Java方法,而把 value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值 为123的动作要到类的初始化阶段才会被执行。
Java中所有基本数据类型的零值:
在“通常情况”下初始值是零值,那言外之意是相对的会有某些“特殊情况”:如果类字段 的字段属性表中存在ConstantValue属性,那在准备阶段变量值就会被初始化为ConstantValue属性所指定 的初始值,假设上面类变量value的定义修改为:
编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据Con-stantValue的设置 将value赋值为123。
解析
解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程
解析阶段中所说的直接 引用与符号引用的关联:
·符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何 形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引 用的目标并不一定是已经加载到虚拟机内存当中的内容。各
·直接引用(Direct References):直接引用是可以直接指向目标的指针、相对偏移量或者是一个能 间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局直接相关的,同一个符号引用在不同虚 拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在虚拟机 的内存中存在。
《Java虚拟机规范》之中并未规定解析阶段发生的具体时间,只要求了在执行ane-warray、 checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invoke-special、 invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield和putstatic这17个用于 操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析。
虚拟机实现可以根据需要来⾃⾏判断,到底是在类被加载器加载时就对常量池中的符号引
⽤进⾏解析,还是等到⼀个符号引⽤将要被使⽤前才去解析它。类似地,对⽅法或者字段
的访问,也会在解析阶段中对它们的可访问性(public、protected、private 、< package
> ) 进⾏检查。
对同⼀个符号引⽤进⾏多次解析请求是很常⻅的事情,除invokedynamic指令以外,虚
拟机实现可以对第⼀次解析的结果进⾏缓存,譬如在运⾏时直接引⽤常量池中的记录,并
把常量标识为已解析状态,从⽽避免解析动作重复进⾏。⽆论是否真正执⾏了多次解析动
作,Java虚拟机都需要保证的是 同⼀个实体中,如果⼀个符号引⽤之前已经被成功解析
过,那么后续的引⽤解析请求就应当⼀直能够成功;同样地,如果第⼀次解析失败了,其
他指令对这个符号的解析请求也应该收到相同的异常,哪怕这个请求的符号在后来已成功
加载进Java虚拟机内存之中。
不过对于invokedynamic指令,上面的规则就不成立了。当碰到某个前面已经由invokedynamic指令 触发过解析的符号引用时,并不意味着这个解析结果对于其他invokedynamic指令也同样生效。因为 invokedynamic指令的目的本来就是用于动态语言支持,它对应的引用称为“动态调用点限定符 (Dynamically-Computed Call Site Specifier)”,这里“动态”的含义是指必须等到程序实际运行到这条指 令时,解析动作才能进行。相对地,其余可触发解析的指令都是“静态”的,可以在刚刚完成加载阶 段,还没有开始执行代码时就提前进行解析。
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7 类符号引用进行,分别对应于常量池的CONSTANT_Class_info、CON-STANT_Fieldref_info、 CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、 CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dyna-mic_info和 CONSTANT_InvokeDynamic_info 8种常量类型。