虚拟机的加载机制(验证)

验证

验证试验阶段的第一步,这一步的目的是为了确保class文件的字节流包含的信息符合当前虚拟机的要求。而且不会危害虚拟机自身的安全

java语言本身是相对安全的语言使用纯粹加瓦语言的代码。无法做到。诸如如何访问数组边界以外的数据,将一个对象转化为它。并为实现的类型跳转到不存在的代码行之类的事情。如果这样做了,边界将拒绝便宜,但前面杰说过,class文件并不一定要求Java源码编译而来。可以使用任何途径产生甚至包含用16进制编辑器直接编写下来的class文件。再次解码语言层面上删除Java代码无法做到的事情是可以实现的,至少遇上。可以表达出来的虚拟机如果不检查输入的字节流,对其完全信任的话,很可能因为载入有害的字节流而导致系统崩溃,所以验证是虚拟机对自身保护的一项重要工作

验证阶段是非常重要的,这个阶段是否严谨直接决定了交往虚拟机是否传授恶意代码攻击,从执行性能的角度讲讲验证阶段的工作量是虚拟机的类加载此系统中账了相当大的一部分。对这个机制的限制知道还比较笼统的规范中列举了一些class文件格式的静态和结构化约束,如果验证到输入的字节流不符合class文件格式的约束,虚拟机就会抛出一个Java。乱点verify,exception异常或此类异常,但具体应当检查哪些方面,如何检查,核实,检查都没有足够的要求和明确的说明,直到2011年发布的Java虚拟机规范Java se第七版。大幅增加了描述验证过程的篇幅,从不到十页增加到100。30页,这时约束和验证规则才变得具体起来,受篇幅所限。当初无法逐条规则去剪剪,当从整体上去看,验证阶段大致会完成下面四个阶段的检验动作。文件格式检验、元数据检验、字节码检验、符号引用检验。

文件格式检验

第一阶段要验证自己留是否符合class文件格式的规范,并且能被当前版本的虚拟机处理,这一阶段可能包含下面这些验证点。
1.是否以魔数xcafebabe开头
2.主次版本号是否在当前虚拟机处理范围之内。
3.常量值的常量中是否有不被支持的常量类型
4.指向常量的各种索引值中是否有只限不存在的常量或不符合类型的常量
5.Class文件中各个部分及文件本身是否有被删除或附加的其他信息。

实际上,第一阶段的验证点远不止如此,上面只是从hot stop虚拟机。源码中宅出的一小部分内容,该验证阶段的主要目的是保证输入的字节流。蓝正确的解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求这阶段的验证是基于二进制字节流进行的。只有通过了这个阶段的验证后,字节流才会进入内存的方法区中进行存储。所以后面的三个验证阶段全部是基于方法区的存储结构进行的。不会再直接操作字节流

元数据验证

第二阶段是对字节码描述的信息进行语义分析,一保障其描述的信息符合Java。语言规范的要求这个阶段可能包含的验证点如下
1.这个类是否有父类,除了object这个他之外所有的累都有父类。
2.这个类的父类是否继承了不允许被继承的类?被final修饰的类
3.如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法
4.类中的字段方法是否与父类产生矛盾,如果覆盖了父类的final字段。或者出现了不符合规则的方法重载,例如方法,参数都一致,当返回类型却不相同等

第二阶段主要的目的是对类的元数据信息进行语义检验保证不存在不符合java语言规范的元数据信息

字节码验证

第三阶段是整个验证过程中最复杂的一个阶段。主要的目的是通过数据流和控制流分析确定程序语义是合法的,符合逻辑的。在第二阶段,对元数据信息中的数据类型做完校验后,这个阶段间对类的方法体进行就业分析保障被救援的类的方法,在运行时不会做出危险虚拟机的安全事件,例如,
1.保证任意时刻操作数栈的数据类型和指令编代码。序列都能配合工作,例如不会出现类似的情况。在操作站放了一个int类型的数据,使用时确按照long来加载到本地变量表中。
2.保证跳转指令不会跳转到方法体以外的字节码的指令上。
3.保障方法体重的类型状况是有效的,例如可以把一个子类对象赋值给父类数据类型。这是安全的,当时把父类对象赋值给此类数据类型,甚至把对象赋值给它,毫无继承关系完全不相干的一个数据类型。这是危险合不合法的
如果一个类方法体的字节码没有通过字节码验证,那肯定是有问题的。但如果一个方法体通过了字节码验证,也不能说明其一定就是安全的。即使自己码验证之中存在了大量的检查,也不能保证这一点,这里涉及那离散数学中一个很著名的问题Halting Problem。通俗一点说,通过查数据校验程序逻辑是无法做到绝对准确的,不能通过程序准确的检查出程序是否能在有限时间之内结束运行。

对于数据流验证的高复杂性迅疾设计团队为了避免过多的时间how在自己把验证阶段在这dk1.6之后的加我c编译器和Java虚拟机中进行了一键优化给封话题的code属性。属性表中增加了一下名为stick map table的属性。这项属性描述了方法体中所有的基本块儿。按照流程,拆封的代码块开始本地变量表和操作数栈。亦友的状态再次解绑,验证期间就不需要根据程序推导这个状态的合法性。只需要检查stick maple table属性中的记录是否合法即可。这样直接把验证的类型推导变成了类型检查,从而节约了一些时间。

理论上的stick map table属性也存在错误或被篡改的可能。所以,是否有可能在恶意篡改了code属性的同时,也生成相应的stick map table属性来骗过迅疾的内心。校验则是虚拟机设计者值得思考的问题

在jdk1.6的hot spot报虚拟机提供了-XX: UseSplitverifer选项来关闭此优化或者使用参数。-XX: FailOverToOldVerfier要求在类型校验失败的时候退回到旧的类型,推导方式进行校验。而在这第一个1.7之后,对于主版本大于50的class文件,使用类型检查来完成数据流分析。校验则是唯一的选择不允许再退回到类型推导的方式

符号引用验证

最后一个阶段的校验发生在迅即将符号引用转换为直接引用的时候,这个转化动作间在链接的第三阶段解析阶段中发生。符号引用验证可以看做是对类自身以外的信息进行匹配性。检验。通常需要检验下列的内容
1.符号应用中通常字符串描述的全限地名是否找到对应的类。
2.在指定类中是否存在符号方法的字段描述符以及简单米长所描述的方法和字段。
3.符号引用中的类字段方法的访问c是否可以被当前类访问

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

猜你喜欢

转载自blog.csdn.net/weixin_39472101/article/details/110308146
今日推荐