Chinaunix首页 | 论坛 | 博客
  • 博客访问: 742314
  • 博文数量: 98
  • 博客积分: 4934
  • 博客等级: 上校
  • 技术积分: 1151
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-12 19:55
文章分类

全部博文(98)

文章存档

2014年(1)

2013年(2)

2012年(4)

2011年(25)

2010年(33)

2009年(33)

分类: Java

2010-08-07 13:27:05

 Class文件校验器
  
   JVM中的class文件校验器,用于保证装在的class文件内容正确,以及它们之间协调一致。它主要负责确保程序执行的安全性,遇见非正常的class文件时,它会提示异常,并阻止JVM运行这些可疑文件。

    class文件由字节码组成,一般由Java编译器生成,当然其他的一些字节码组成的文件也能充当class文件,这就要看它的具体内容。如果这个class文件由某个带有bug的编辑器生成,或者是被人恶意篡改了一些内容,比如在一个方法里悄悄加上跳转到方法之外指令,那么在执行这个class文件时,JVM将崩溃。文件校验器正是要保证class文件的内容、结构、完整性都符合JVM执行的要求。因此,它会在字节码执行之前进行class文件扫描,扫描分为四次:

    1,class文件结构检查

    此次扫描目的是确定class文件符合Java class的基本结构,比如文件是否以0XCAFEBABE这四个字节开头,文件是否包含正确的版本号和此版本号。我们在运行一些程序时偶尔会遇见java.lang.UnsupportedClassVersionError 错误,然后后面跟着一个版本号错误的提示信息,这就是class文件结构检查时抛出的错误,表明class文件是由另一个版本的Java编译器生成的,而这个版本的编译器不在当前的JVM支持范围以内。另外,这次扫描还会检查文件的完整性(头尾完整),以及文件中对数据结构长度信息的描述是否与文件内容总长度符合,以确保文件正确的定义了一个新的class类。

    2,类型数据的语义检查

    第二次扫描针对文件的各个组成部分,比如类的方法和属性。方法的返回类型、参数都会存储为一个字符串,这个字符串必须满足的Java规则,校验器将会校验所有的方法描述符(返回类型、参数、参数个数等),确定它们符合Java语法。另外,它还会对类本身应该遵循的规则进行校验,必须是否是Object或由Object类继承而来,是否继承了final类型的类,是否重写了final类型的方法,常量的定义是否正确。尽管编译器已经进行语法的检验,但是JVM还是需要再次进行校验,确定这些class文件是由可靠的编译器生成的。如果某位程序员恶意修改了编译器,使得它可以编译一些语法错误的Java文件,那么在这里将校验失败,并抛出一个错误。

    3,字节码验证

    class文件中,方法是由字节码流来表示的,这次扫描中JVM将对对字节码流进行分析。字节码流是由一些成为操作码组成的序列,而操作码后面则跟着一个或多个操作数。操作码、操作数的概念见

 

    原文关于字节码验证一段:

写道
The bytecode streams that represent Java methods are a series of one-byte instructions, called opcodes, each of which may be followed by one or more operands. The operands supply extra data needed by the Java Virtual Machine to execute the opcode instruction. The activity of executing bytecodes, one opcode after another, constitutes a thread of execution inside the Java Virtual Machine. Each thread is awarded its own Java Stack, which is made up of discrete frames. Each method invocation gets its own frame, a section of memory where it stores, among other things, local variables and intermediate results of computation. The part of the frame in which a method stores intermediate results is called the methodís operand stack. An opcode and its (optional) operands may refer to the data stored on the operand stack or in the local variables of the methodís frame. Thus, the virtual machine may use data on the operand stack, in the local variables, or both, in addition to any data stored as operands following an opcode when it executes the opcode.

    大致意思是:class文件中Java的方法是字节码流代替的,字节码流就是流操作码序列,而操作码就是单字节指令,JVM中定义了220条这样的指令。操作码后面会紧随一个或多个操作数,操作码决定要完成的操作,操作数指参加运算的数据及其所在的单元地址。JVM会创建线程来执行字节码流,其实就是一个接一个执行字节码流中的操作码。每个线程都会分配一个栈,栈由许多帧组成。每次方法调用都将产生一个新的帧,帧就是一块内存区域,用来存储中间结果和局部变量。帧里面用来存储中间结果的部分称为操作数栈,操作码和操作数可能会用到操作数栈中的数据或者帧中的局部变量。这样,在执行操作码是时,JVM不但可以使用紧随在其后的操作数,还可以使用操作数栈中的数据,以及局部时变量。

    校验器在这趟扫描里要做大量的工作,在执行字节码流中的一个特定操作码时,它保证无论如何执行,总能在操作数栈中得到一致的数据。校验器将确保局部变量在使用之前已经赋值,类的成员变量的赋值正确,类的成员方法的调用符合参数要求。校验器还必须保证,操作码、操作数以及在执行中将产生的操作数栈中的数据正确。字节码校验还有其他校验工作要做。

    完成字节码校验以后,它将确保这个class文件安全的在JVM上执行。

    经过1,2,3次扫描,class文件校验器可以保证class文件在结构到内容正确,满足java规范,包含的字节码可以在JVM上安全的执行。如果扫描中出现了问题,则会抛出错误,并阻止JVM执行。

    4,符号引用的校验

    前三次扫描发生在class文件执行以前,而第四次则发生在动态连接时。

    动态链接时,class中的引用的符号将被解析,文件校验器必须保证引用正确,JVM将开始校验被引用的class文件。因为涉及到其他的class文件,类装载器可能需要装载这个class,JVM采用延迟装载机制,一直到class被使用时才会装载。有时为了提高装载速度,会进行预先装载,尽管存在预先装载,JVM还是表现出延迟装载的特征。比如预先装载时有一个引用的class文件找不到,那么它不会立即抛出NoClassDefFoundError,而是等到这个class被使用时再抛出。这样第四次扫描可能会紧跟着前三次进行,也可可能在前三次扫描完毕以后很久再执行。

    符号引用校验包括符号的引用信息的校验,引用是否合法。比如引用的方法名称、参数是否正确,类名、成员变量名是否正确等。JVM在解析引用时进行以下操作:

    A,查找被引用的类,必要的话装载它

    B,将符号引用替换为直接引用,比如将指向方法的引用——方法名替换为指向方法的指针或偏移量。

    JVM将记住这个直接引用,以便下次遇到相同的引用时,不必再进行解析。

    另外,在第四次扫描中,校验器还将检查兼容性,比如在大型的系统中,class被拆分为许多包,其中一些包的修改并不会导致所有引用它的包的重新编译,这时候在运行时就会提示一些java.lang.NoSuchMethodError,

java.lang.NoSuchFieldError错误。

From: http://salever.javaeye.com/blog/717670

阅读(1010) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~