Java虚拟机(2) 之类加载的过程

2018-01-31 11:17:44来源:oschina作者:戥火燃芯人点击

分享

一、加载


在加载阶段虚拟机需要完成以下三件事:


1.通过一个类的 全限定 名称来获取此类的二进制字节流 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口


通常来讲,一个类的全限定名称可以从zip、jar包中加载,也可以从网络中获取,也可以在运行的时候生成(这点最明显的技术体现就是反射机制)


对于类的加载,可以分为数组类型和非数组类型 1.对于非数组类型可以通过系统的引导类加载器进行加载,也可以通过自定义的类加载器进行加载 2.对于数组类型,数组类本身不通过类加载器进行加载,而是通过Java虚拟机直接进行加载的 当数组去除所有维度之后的类型最终还是要依靠类加载器进行加载的,所以数组类型的类与类加载器的关系还是很密切的


通常一个数组类型的类进行加载需要遵循以下的原则: 1.如果数组的组件类型(也就是数组类去除一个维度之后的类型,比如对于二维数组,去除一个维度之后是一个一维数组)是引用类型, 那么递归采用上面的过程加载这个组件类型 2.如果数组类的组件类型不是引用类型,比如是基本数据类型,Java虚拟机将把数组类标记为与引导类加载器关联 3.数组类的可见性与组件类型的可见性是一致的。如果组件类型不是引用类型,那么数组类的可见性是public,意味着组件类型的可见性也是public


加载阶段与连接阶段是交叉进行的,所以可能加载阶段还没有完成,连接阶段就已经开始。但是即便如此,记载阶段与连接阶段之间的开始顺序仍然保持着固定的顺序


二、验证 验证阶段的目的是为了确保Class字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全


Java语言具有相对的安全性 这里的安全性体现为两个方面:一是Java语言本身特性,比如Java去除指针,这点可以避免对内存的直接操作; 二是Java所提供的沙箱运行机制,Java保证所运行的机制都是在沙箱之内运行的,而沙箱之外的操作都不可以运行


但是需要注意的是Java虚拟机处理的Class文件并不一定都是从Java代码编译而来, 完全可能是来自其他的语言, 甚至可以直接通过十六进制编辑器书写Class文件(当然前提是编写的Class文件符合规范)。 从这个角度讲,其他来源的Class文件是不可能都保证其安全性的。 所以如果Java虚拟机都信任其加载进来的Class文件,那么很有可能会造成对虚拟机自身的危害。


虚拟机的验证阶段主要完后以下4项验证:文件格式验证、元数据验证、字节码验证、符号引用验证


(2.1)、文件格式验证 这里的文件格式是指Class的文件规范, 这一步的验证主要保证加载的字节流符合Class文件的规范以及保证这个字节流可以被虚拟机接受处理。 (Class文件的每一个字节表示的含义都是确定的。比如前四个字节是否是一个魔数等)


在Hotspot的规范中,对文件格式的验证远不止这些,但是只有通过文件格式的验证才能进入方法区中进行存储。所以自然也就知道,后面阶段的验证工作都是在方法区中进行的


(2.2)、元数据验证 元数据可以理解为描述数据的数据 更通俗的说,元数据是描述类之间的依赖关系的数据


元数据验证主要目的是对类的元数据信息进行语义校验,保证不存在不符合Java语言规范(Java语法)的元数据信息。


具体的验证信息包括以下几个方面: 1.这个类是否有父类(除了java.lang.Object外其余的类都应该有父类) 2.这个类的父类是否继承了不允许被继承的类(比如被final修饰的类) 3.如果这个类不是抽象类,是否实现了其父类或者接口中要求实现的方法 4.类中的字段、方法是否与父类产生矛盾(比如是否覆盖了父类的final字段)


(2.3)、字节码验证 主要对类的方法体进行校验分析


(2.4)、符号引用验证 符号引用是Class文件的逻辑符号,直接引用指向的方法区中某一个地址, 在解析阶段,将符号引用转为直接引用,这里只进行转化前的匹配性校验。 符号引用验证主要是对类自身以外的信息进行匹配性校验,比如符号引用是否通过字符串描述的全限定名是否能够找到对应点类


进行符号引用验证的目的在于确保解析动作能够正常执行,如果无法通过符号引用验证那么将会抛出java.lang.IncomingChangeError异常的子类


符号引用(Symbolic Reference) 符号引用以一组符号来描述所引用的目标,符号引用可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可(符号字面量,还没有涉及到内存)。 符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载在内存中。 各种虚拟机实现的内存布局可以各不相同,但是他们能接受的符号引用必须都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。


直接引用(Direct Reference) 直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄(可以理解为内存地址)。 直接引用是与虚拟机实现的内存布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般都不相同, 如果有了直接引用,那引用的目标必定已经在内存中存在。


三、准备


四、解析


五、初始化

微信扫一扫

第七城市微信公众平台