JVM学习笔记2(class文件校验器)

来源:互联网 发布:淘宝的一件代发怎么做 编辑:程序博客网 时间:2024/05/21 01:29

沙箱组成部件的第二部分:class文件校验器。

沙箱限制了下载的任何病毒或者其他恶意的、有漏洞的代码,使得它们不能对计算机进行破坏。如果说类装载器是为了限制恶意代码的,class文件校验器就是为了保证class文件有正确的结构,能够正确运行。如果class文件校验器在class文件中发现了问题,它将抛出异常。

总的来说class文件校验器是在字节码执行前执行的,一次性对字节码进行分析,确保程序的健壮性。

class文件要进行四趟扫描。第一趟扫描是在类被装载时进行的。第二和第三趟是在连接过程中进行的。第四趟是在进行动态连接的过程中解析符号引用时进行的。

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

目的:在第一趟扫描中,对每一段将被当作类型导入的字节序列,class文件校验器要保证这个字节序列正确地定义了一个新的类型,它必须遵守Java的class文件的固定格式,这样它才能被编译成在方法区中的(基于实现的)内部数据结构。

第一趟扫描是在二进制数据上进行的。主要要进行下面三个方面的检查。

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

目的:简单来说就是确定class文件遵守了Java语言应该在编译时遵守的强制规则。比如:

  • 方法描述符(它的返回类型,以及参数的类型和个数)在class文件中被存储为一个字符串,这个字符串必须符合特定的上下文无关文法。
  • 除Object类以外的所有类,都必须有一个超类。
  • 检查final类没有被子类化,final方法没有被覆盖。
  • 检查常量池中的条目是合法的,常量池的所有索引必须指向正确类型的常量池条目。

因为校验器并不能确定class文件是否是由一个善意的、没有漏洞的编译器产生的,所以它会检查每个class文件,以确保这些规则得到遵守。

这一趟不需要查看字节码。

第三趟:字节码验证

目的:检查字节码,确保某些符合特定规则的字节码能够正确执行。是针对各种变量的数值、类型正确性和操作码合法性的检查,

先定义几个概念:

  • 字节码流:操作码+操作数。
  • 栈帧:每一个方法调用获得一个自己的栈帧。栈帧其实就是一个内存片段,其中存储着局部变量和计算的中间结果。
  • 局部变量栈:存储局部变量形参等。
  • 操作数栈:存储计算的中间结果。

字节码流代表了Java的方法。执行字节码时,依次执行每个操作码,这就在Java虚拟机内构成了执行的线程。每个线程被授予自己的Java栈,这个栈由不同的栈帧组成。其余结构如上图所示。

字节码检查器要进行大量的检查,确保采用任何路径在字节码流中都得到一个确定的操作码,确保操作数栈总是包含正确的数值以及正确的类型。它必须保证局部变量在赋予合适的值以前不能被访问,而且类的字段中必须总是被赋予正确类型的值,类的方法被调用时总是传递正确数值和类型的参数。字节码检验器还必须保证每个操作码都是合法的,即都有合法的操作数,以及对每一个操作码,合适类型的数值位于局部变量中或是在操作数栈中。这些仅仅是字节码检验器所做的大量检验工作中的一小部分,在整个检验过程通过后,它就能保证这个字节码流可以被java虚拟机安全的执行。

在第一、第二、第三趟扫描中,class文件检验器可以保证导入的class文件构成合理,内在一致,符合Java编程语言的限制,并且包含的字节码可以被Java虚拟机安全地执行。

第四趟:符号引用的验证

目的:保证被引用的其它class文件是正确的。

第四趟检查是在动态连接时使用的。动态连接指的是将一个符号连接解析为直接引用的过程。举例来说,A类中引用了一个B类,装载A类时并不会装载B类,只会把B类看做一个符号引用,当要执行A类时,解析符号引用:

  1. 查找被引用的类(如果必要的话就装载它)。
  2. 将符号引用替换为直接引用,例如一个指向类、字段或方法的指针或偏移量。

如果以后还要使用B类,可以直接使用B类,而不用再次解析。

此时class文件校验器保证这个引用不是非法的——例如,这个类不能被装载,或这个类的确存在,但是不包含被引用的字段或方法。

这里还有一个二进制兼容性的规则。也就是B类如果被修改,它要与A类兼容。假设A类使用了B类中的某个方法。程序运行结束后,B类做了修改——它删除了这个方法,此时程序再次编译,注意B类并没有引用A类,所以只需再次编译B类即可。此时A类调用B类的这个方法就会抛出一个异常。

为了能方便的修改类库的代码,java编程语言被设计成允许对一个类做多种修改,但并不要求对依赖于它的那些类进行重编译。java语言规范中列出了用户可以做的多种改动,这些改动称为二进制兼容性规则。这些规则明确地定义了:在一个类中,哪些可以被修改、增加和删除,而并不破坏这个被修改的类与依赖于它的那些事先已经存在的类之间的二进制兼容性。

总结

第一趟扫描:确定是class文件,确认长度、版本等方面。

第二趟扫描:检查类,方法等是否符合规范。

第三趟扫描:检查字节码,检查操作码,操作数是否符合规范。

第四趟扫描:确定引用是正确的,比如存在类名,方法等。


0 0
原创粉丝点击