Java (JVM) 中的验证
在 JVM 中的类加载器将 .class 文件的字节码加载到机器后,验证者首先检查字节码的有效性,这个过程称为验证。验证器在链接时执行尽可能多的检查,以便可以消除解释器在运行时执行的昂贵操作。它提高了口译员的表现。
验证者执行的一些检查:
- 未初始化的变量
- 不违反私有数据和方法的访问规则。
- 方法调用匹配对象引用。
- 没有操作数堆栈上溢或下溢。
- 所有Java虚拟机指令的参数都是有效类型。
- 确保最终类没有子类化并且最终方法没有被覆盖
- 检查所有字段引用和方法引用是否具有有效的名称、有效的类和有效的类型描述符。 (来源)
如果这些检查中的任何一个失败,JVM 会抛出“Java.lang.VerifyError”错误。但是,我们可以使用禁用这些检查
java -noverify VerifyGeekFile
有人可能会想,由于编译器会在生成 .class 文件之前检查上述这些验证,因此可能会如何操纵程序。但是类文件在 JVM 加载之前很容易被更改。类文件中使用的字节码有据可查,对十六进制有一定了解的人可以更改 .class 文件中十六进制代码的值,从而改变程序的行为。
例如:Web 浏览器中的小程序不下载源代码,而是下载预编译的类文件。您计算机上的浏览器会确定此类文件是否值得运行,或者该文件是否已被“恶意编译器”利用。
考虑这个简单的程序
// A Java program to demonstrate working
// of the verification process
class VerifyGeekFile
{
// your initial bank bal
private float bal;
// Method to deposit money
float depositBalance(int bal)
{
int myBal = bal;
this.bal += myBal;
return this.bal;
}
// Driver Method
public static void main(String[] args)
{
VerifyGeekFile obj = new VerifyGeekFile();
System.out.println(obj.depositBalance(4000));
}
}
Output
4000.0
然后在命令行中执行此命令以助记符形式查看字节码:-
javap -c VerifyGeekFile
输出 :
float depositBalance(int);
Code:
0: iload_1
1: istore_2
2: aload_0
3: dup
4: getfield #2 // Field bal:F
7: iload_2
8: i2f
9: fadd
10: putfield #2 // Field bal:F
13: aload_0
14: getfield #2 // Field bal:F
17: freturn
在这里,如果我们更改 myBal 的初始值或使用十六进制编辑器使其未初始化,则会返回意外结果。 Java验证过程保护我们免受所有这些陷阱的影响。
参考:
http://www.informit.com/articles/article.aspx?p=1187967&seqNum=2
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10