【问题标题】:Java bytecode: types of local variables?Java字节码:局部变量的类型?
【发布时间】:2013-04-06 18:21:26
【问题描述】:

据本文http://slurp.doc.ic.ac.uk/pubs/observing/linking.html#assignment

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

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

【问题讨论】:

  • 我怀疑在compile-time 检查了类型值,导致代码不再需要类型值信息,只需要二进制数据。然而,方法参数和返回值在run-time 调用,并且需要用于类型检查的信息
  • 谢谢,有道理!
  • 因为它就是这样。 JDK 验证程序使用数据流分析来找出每个数据使用的“到达”类型。参数和实例/静态变量需要声明类型才能输入数据流。计算结果的类型OTOH可以由规则确定。除此之外,本地变量在编译时通常会消失在堆栈值中。

标签: java jvm bytecode


【解决方案1】:

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

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

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

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

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

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

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

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

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

【讨论】:

  • 值得注意的是,returnAddress 类型仅与 jsrjsr_w 操作码一起使用,它们在 Java 6 中被禁止。现代字节码操作的值永远不会包括returnAddress.
  • @Tom 它们不能在主版本高于 49 的类文件中使用。但您仍然可以在现代 VM 上运行版本号较低的类文件。如果您愿意,可以将版本设置为低至 45.0,这样做会在 Hotspot 中启用一些有趣的未记录行为。如果您只考虑 Java 编译的字节码,那么 javac 生成的字节码将永远不会做很多事情。但字节码黑客仍然对它感兴趣。
  • 真的!我们不得不保留这个杂乱无章的东西有点遗憾,因为它使解释字节码的工作原理变得更加复杂。
  • @Tom 更有趣的是,如果您查看源代码,就会发现有些代码似乎是在 Java 还没有被称为 Java 之前遗留下来的。甚至 Hotspot 开发人员也不确定它为什么还在那里。
  • @Antimony 很有用,阅读愉快!一些参考将使这个答案更加完美!
【解决方案2】:

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

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

【讨论】:

  • 我很欣赏你的专业知识。对我来说,字节码就像魔法……同样神秘。
  • 我真的没有专业知识 - 我已经阅读了规范,但仅此而已。
  • StackMapTables 仅在每个基本块的开头提供值的类型,因此需要数据流分析来重建所有值的类型。特别是,线性代码甚至可能没有StackMapTable 属性。它只是更快,因为数据流不再需要在循环或分支的情况下进行多次传递。
  • 啊,这只是每个基本块,是吗?在这种情况下,确实会有类型未被覆盖的变量。
【解决方案3】:

Java bytecode 保留关于 fields 的类型信息, method returnsparameters 但它没有, 如你所愿, 包含local variables 的类型信息。

Java 类文件中的类型信息呈现任务 bytecode 的反编译比反编译的容易 machine code。因此,反编译 Java 字节码需要 大多数局部变量类型的分析,基于堆栈的扁平化 loopsconditionals 的指令和结构。 然而,字节码反编译的任务很多 比编译难。你会经常看到反编译器 无法完全发挥其预期功能

【讨论】:

    猜你喜欢
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-27
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多