标签:存在 详细 之间 问题 obj 开始 语言 执行 实例
Java虚拟机通过装载、连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用。其中装载就是把二进制形式的Java class文件读入Java虚拟机中去;连接把读入虚拟机的二进制形式的Java class文件合并到虚拟机的运行时的状态中去。连接可以分为三个子步骤,验证,准备和初始化。验证步骤确保了Java类型数据格式正确并且适于Java虚拟机使用。准备为该类型分配内存(如为类变量分配内存)。解析负责将常量池中的符合引用转换为直接引用,虚拟机的实现可以推迟解析这一步骤。即当运行中的程序真正使用某个符号的时候再去解析它。(符号引用变为直接引用)。初始化为类变量赋以适当的初始值。
下面就这5个步骤进行详细的解析:
装载阶段由三个动作完成
装载的最终目的就是创建java.lang.class的实例对象,解析二进制数据流为方法区的内部数据结构,并在堆上面建立一个Class对象。Class对象是Java程序与内部数据结构之间的接口。
Java类型要么被启动类装载器装载,要么被用户自定义的类加载器加载。
类加载器并不是在某个类型首次主动使用的时候加载它。可以提前预判,预加载这个类型。如果预加载的时候出现问题,不会立即报错。直到程序主动使用该类才报错。也就是说程序已知不主动使用该类类型,类加载器就一直不报错。
装载时会做两项检查,虽然在验证阶段之前进行,但逻辑上属于验证阶段。
类型被加载后,虚拟机开始连接,连接第一步是验证,即确认它是否符合Java语言的语义,并且不会危及虚拟机的完整性。
验证的时机和方式,不同虚拟机有着不同的实现。虚拟机规范只是规定了虚拟机在验证过程中可以抛出的异常以及在何种情况下必须抛出它们。
验证阶段往往会验证如下内容:
上述任务最复杂的是最后一个,检查字节码的完整性。不能因为挑战指令超出了方法末尾导致了虚拟机崩溃。虚拟机必须在验证阶段检查出这样的字节码是非法的,从而抛出一个错误。
不过虚拟机规范并未规定字节码验证必须在验证阶段执行,也可以在执行每条语句的时候单独进行验证。Java虚拟机指令集当初设定目标是字节码流一次性使用数据流分析器进行验证。不过运行时候进行验证,会使得Java程序运行速度提高。当使用数据流分析器进行字节码验证时候,可能为了确保符合Java语言的语义而装载其他的类。比如加载了Float类,必须还加载其父类Number保证其不是一个final类。
验证阶段之后可能还会进行一项检查,那就是符号引用的验证。虚拟机在将符号引用替换为直接引用,需要检查该元素的存在性以及访问的权限。逻辑上属于验证,但往往在解析阶段发生。
在准备阶段,Java虚拟机为类变量分配内存,并设置默认的初始值。在正式初始化阶段之前,类变量都没有被正式初始化为真正的初始值。
准备阶段,干的另外一件事就是可能为一些数据结构分配内存,进而提高程序运行的性能。比如方法表的建立,它包含了指向类中每一个方法包括从超类继承方法的指针。从而可以在执行继承方法时候不需要搜索超类。
解析过程就是在类型的常量池中寻找类、接口、自动和方法的符合引用,把这些符号引用变成直接引用的过程。还记得逻辑上属于验证的符合引用的验证么?解析什么时候执行是虚拟机自己决定的,可以在初始化阶段后面执行。
初始化阶段为类变量赋予正确的初始值。在准备阶段,类变量被赋予了默认值,在这一阶段,类变量被赋予正确的初始值,即程序员希望这个类变量具备的起始值。
具体的初始化过程参见下一篇博客《浅谈类加载的初始化阶段》
浅谈Java类型装载、连接与初始化--转自https://blog.csdn.net/lincolnmi/article/details/50538016
标签:存在 详细 之间 问题 obj 开始 语言 执行 实例
原文地址:https://www.cnblogs.com/wood-lin/p/9019419.html