有人能列出字节码验证器必须执行的主要任务以保证程序的正确性吗?在JVM规范中是否定义了标准的最小责任集合?我还想知道验证是否跨越其他阶段,如加载和初始化。
这在JVM规范:第4.10章“验证类文件”中有详细说明。
该页面的主要内容介绍了类型安全的各个方面。为了检查程序是否类型安全,验证器需要确定操作数栈上每个程序点中所存储的操作数的类型,并确保它们与相应指令期望的类型匹配。
其它它验证的内容包括但不限于以下内容:
分支必须在方法的代码数组范围内。
所有控制流指令的目标都是指令的起始位置。对于wide指令,wide操作码被视为指令的起始位置,而被该wide指令修改的操作码不被视为指令的开始位置。不允许分支进入指令中间。
任何指令都不能访问或修改索引大于或等于其方法所分配的局部变量数的局部变量。
对常量池的所有引用必须是适当类型的条目。 (例如,getfield指令必须引用字段。)
代码不能在指令中间结束。
执行不会跌落代码的结尾。
对于每个异常处理程序,该处理程序所保护的代码的起始和结束点必须位于指令的开头或,在结束点的情况下,紧接着代码的末尾。起始点必须在结束点之前。异常处理程序代码必须从有效指令开始,并且不能从被wide指令修改的操作码开始。
最后一步,验证器还会执行数据流分析,以确保任何指令都没有引用任何未初始化的局部变量。
或者您可能想要查看James Gosling撰写的Java语言环境白皮书。
字节码校验器遍历字节码,构造类型状态信息并验证所有字节码指令参数的类型。
图示展示了从Java语言源代码通过Java编译器到类加载器和字节码校验器的数据和控制流,然后到包含解释器和运行时系统的Java虚拟机。重点是Java类加载器和字节码校验器不对字节码流的主要来源——代码可能来自本地系统,也可能绕了半个地球而来——进行任何假设。字节码校验器充当了一种守门人的角色:它确保传递给Java解释器的代码处于适合执行的状态,并且可以在不破坏Java解释器的情况下运行。导入的代码在通过验证之前不允许以任何方式执行。验证器完成后,已知一些重要的属性:
- 没有操作数堆栈溢出或下溢
- 所有字节码指令的参数类型都已知且始终正确
- 对象字段访问是合法的——私有、公共或受保护的
虽然所有检查看起来都非常详细,但是当字节码校验器完成其工作时,Java解释器可以继续前进,知道代码将安全运行。了解这些属性使Java解释器更快,因为它不需要检查任何内容。没有操作数类型检查和堆栈溢出检查。因此,解释器可以在不影响可靠性的情况下以全速运行。