Java虚拟机学习笔记之class文件检验器


在编写java程序的时候,使用的API类不正确,引用的自定义类的包路径不正确以及定义的变量不存在或者不正确等等,在这个时候,对于Eclipse这样的IDE工具在保存的时候会显示错误,表示在.java文件编译成以.class时发生的编译错误而不能生产正确的class文件。

但是正确的编译成class文件就一定可以了吗?当然对于我们这样的学习的朋友来说没什么问题,如果真在某些应用中,如果有的class文件需要从网络中获得class文件装载到虚拟机中运行。java虚拟机并不知道class文件的来源以及是否是正确的编译,如果获得class文件是黑客恶意编译的class文件,结果可能就不可想象。

class文件(java程序)是动态的装载,连接到解析执行。如何保证调用的class文件以确保这些定义的类型安全使用,作为java虚拟机必须实现-----“class文件检验器”。又做了哪些检验,是如何去做的呢?

★class文件检验器

和类装载器一起,保证装载的class文件内容有正确的内部结构,并且这些class文件相互协调一致。如果存在问题就会抛出异常。class文件检查器的安全目标之一是程序的健壮性。在字节码执行之前,必须完成大部分的检验工作,即只在执行前而不是在执行中队字节码进行一次分析,每一次遇到一个跳转指令时都进行检验。class文件检验器的操作分四趟:

第一趟扫描时在类被装载时进行的,检查这个class文件的内部结构,以保证它可以被安全的编译。第二和三扫描时在连接过程中进行的,确认类型数据遵从java编程语言的语言,包括检验它所包含的所有字节码的完整性。第四趟是在进行动态链接的过程中解析符号引用时进行的,确认被引用的类,字段以及方法确实存在。

1.第一趟:class文件的结构检查

确认被当做类型导入的字节序列是否符合java class文件的基本结构。

1)确认class文件是否以OxCAFEBABE开头。魔数OxCAFEBABE,可以明显区分文件是否有问题或者是不是class文件。

2)确认class文件中声明的主版本号和次版本号。必须在这个java虚拟机实现可以支持的范围内。

3)根据class文件中每个组成部分的类型和长度来确定文件总长度。检查装入的文件,其长度和内容是否一致。

正确的class文件被编译成在方法区中(基于实现的)内部数据结构。

2.第二趟:类型数据的语义检查

在基于实现的方法区中进行检查:

1)查看每个组成部分,确认是否是其所属类型的实例,结构是否正确。

方法描述符(返回类型,以及参数的类型和个数)在class文件中被存储为一个字符串,这个字符串必须符合特定的上下文无关文法。做这样的检查可以确认每个方法描述符都是符合特定语法的,格式正确的字符串。

2)检查类本身是否符合特定的条件,由java编程语言规定的。比如:除Object类外,都必须存在一个超类。final(最终的)类没有子类,final方法没有被覆盖等等。

3.第三趟:字节码验证

此趟扫描注意力在字节码上,因此被成为“字节码验证”。java虚拟机对字节流进行数据流分析,这些字节流代表的是类的方法。

字节码流:代表了java的方法,它是由被成为操作码的单字节指令组成的序列,每个操作码后都跟着一个或多个操作数。

操作数:用于在java虚拟机执行操作码指令时提供所需的额外的数据。

执行字节码时,依次执行每个操作码,这就在java虚拟机内构成了执行的线程。每个线程被授予自己的java栈,这个栈由不同的栈帧构成。每个方法调用将获得一个自己的栈帧。

栈帧:一个内存片断,其中存储着局部变量和计算的中间结果。

操作数栈:在栈帧中,用于存储方法的中间结果的部分。

操作码和它的可选的操作数可能指存储在操作数栈的数据,或者存储在方法栈帧中的局部变量中的数据。这样在执行一个操作码时,除了可以使用紧随其后的操作数,虚拟机还可以使用操作数栈中的数据,或局部变量中的数据,或者两者都用。

1)确保操作数栈总是包含正确的数值以及正确的类型。

2)确保每个操作码都有合法的操作数,每个操作码,合适类型的数值位于局部变量中或是操作数栈中。

在整个检验过程通过后,可以保证这个字节码流被java虚拟机安全的执行。字节码并试图检验出所有的安全问题。如果那样可能会遇到“停机问题”。

4.第四趟:符号引用的验证

在动态连接的过程中,class文件中包含的符号引用被解析时所做的检查。在这趟检查中,java虚拟机将追踪引用

1)被验证的class文件

2)被引用的class文件

因此在这趟扫描中可能需要装载新的类。大多数的java虚拟机采用的是延迟装载,即真正使用时才装载。

动态连接是将符号引用解析为直接引用的过程。当java虚拟机执行字节码时,如果它遇到一个操作码,这个操作码第一次使用一个指向另一个类的符号引用,虚拟机就必须解析这个符号引用。

1)查找被引用的类(如果必要的话就装载它)

2)将符号引用替换成为直接引用,再次遇到相同引用无需解析可直接使用。

当某个引用时非法引用时,for example:这个类不能被装载,或者不存在,但是不包含被引用的字段或者方法-----class文件检验器将抛出一个错误。在第四趟的扫描中,如果某个符号引用匹配不到正确的方法,即扫描失败,java虚拟机将抛出一个NoSuchMethodError。

相关内容