📜  Java (JVM) 中的验证

📅  最后修改于: 2022-05-13 01:55:27.106000             🧑  作者: Mango

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