10

根据这篇文章http://slurp.doc.ic.ac.uk/pubs/observing/linking.html#assignment

由于 Java 代码和字节码之间的信息差异(字节码不包含局部变量的类型),验证者不需要检查子类型是否分配给局部变量或参数。

我的问题:为什么字节码不包含局部变量的类型信息,而它确实包含参数和返回值的类型信息?

4

3 回答 3

6

首先,有几种不同的类型概念。有编译时类型,其中包括泛型。但是,泛型在编译后不存在。

有验证推断变量的静态类型,可以是 int、float、long、double、returnaddress 或对象引用。对象引用附加了一个上限类型,因此所有引用都是java/lang/String例如的子类型。字段还可以具有以下一种短类型:byte、short、char 或 boolean。出于执行目的,它们与 int 相同,但存储不同。

最后是运行时类型,它与验证的静态类型相同,但在对象引用的情况下,表示被引用实例的实际类型。请注意,由于验证者的惰性,在某些情况下运行时类型实际上可能不是已验证类型的子类型。例如,声明类型的变量Comparable实际上可以保存 Hotspot 中的任何对象,因为 VM 在验证时不检查接口。

编译时间信息不会保留,除非通过反射和调试的可选属性。这是因为没有理由保留它。

局部变量没有明确的类型信息(新的 StackMapTable 属性除外,但这是技术性的)。相反,当加载类时,字节码验证器通过运行静态数据流分析来推断每个值的类型。这样做的目的不是为了捕捉编译时类型检查之类的错误,因为假设字节码在编译时已经通过了此类检查。

相反,验证的目的是确保指令不会对 VM 本身造成危险。例如,它需要确保您没有采用整数并将其作为对象引用进行干预,因为这可能导致任意内存访问和入侵 VM。

因此,虽然字节码值没有显式类型信息,但它们确实具有隐式类型,这是静态类型推断的结果。这个细节根据每个 VM 的内部实现细节而有所不同,尽管它们应该遵循 JVM 标准。但是您只需要担心手写字节码中的这一点。

字段具有明确的类型,因为 VM 需要知道其中存储了哪种类型的数据。方法参数和返回类型被编码在所谓的方法描述符中,也用于类型检查。它们不可能自动推断,因为这些值可以来自或去任何地方,而类型检查是基于每个类完成的。

PS 在谈到验证类型时,我遗漏了一些小细节。对象类型还跟踪它们是否已初始化,如果未初始化,则由哪个指令创建它们。地址类型跟踪创建它们的 jsr 的目标。

于 2013-04-14T18:28:56.420 回答
3

那是一张很旧的纸。当前的类文件确实包括本地和堆栈变量的类型。类型不存储在方法字节码中,而是存储在附加到方法的StackMapTable属性中。

在没有 的情况下,可以(并且总是)通过数据流分析来重建所有局部变量和堆栈元素的类型StackMapTable,但计算成本很高。StackMapTable可以更快地验证带有 s 的代码。尽管我不得不承认,我看不出验证StackMapTables 比进行分析更快,但我对此几乎一无所知。

于 2013-04-14T18:53:34.767 回答
2

Javabytecode保留了关于 的类型信息fields, 但正如您所问method returns的,parameters它不 包含 的类型信息。local variables

Java 类文件中的类型信息使得反编译的任务bytecode比反编译 machine code. 因此,反编译 Java 字节码需要分析大多数局部变量类型、扁平化基于堆栈的指令以及 和 的loops结构conditionals。然而,字节码反编译的任务比编译要困难得多。您会经常看到反编译器无法完全执行其预期功能

于 2013-04-14T17:00:49.350 回答