【问题标题】:What is a stack map frame什么是堆栈图框
【发布时间】:2014-08-03 23:08:10
【问题描述】:

我最近一直在查看Java Virtual Machine Specifications (JVMS) 以试图更好地了解是什么让我的程序正常工作,但我发现了一个我不太了解的部分......

4.7.4 部分描述了 StackMapTable 属性,并且在该部分该文档详细介绍了堆栈映射框架。问题是它有点罗嗦,我最好通过例子来学习;不是通过阅读。

我知道第一个堆栈映射框架是从方法描述符派生的,但我不明白如何(应该解释为here。)另外,我不完全理解堆栈映射框架的作用。我会假设它们类似于 Java 中的块,但看起来好像你不能在彼此内部拥有堆栈映射框架。

无论如何,我有两个具体问题:

  • 堆栈图框有什么作用?
  • 第一个堆栈地图框是如何创建的?

还有一个一般性问题:

  • 谁能提供一个比 JVMS 中给出的解释更简洁、更容易理解的解释?

【问题讨论】:

  • @EJP 这是我正在做的事情。这是我决定首先阅读 JVMS 的主要原因之一。
  • @EJP 我也一直在阅读JVM规范,相信我,这不仅仅是阅读规范,例如:了解类型验证如何工作的部分(与此相关问题)你需要有 Prolog 编程的基本知识......所以我认为这个问题/答案值得在 Stackoverflow 中提出
  • @morgano,我发现忽略 Prolog 的东西并专注于经典推理验证器会更有帮助。新的验证器非常相似,他们只是决定在 Prolog 的 200 页中指定它,而不是像旧的那样使用模糊的英文描述
  • @Antimony 确切地说,这就是这个问题的目的,将正式规范翻译成简单的英语。
  • 我已经尝试在这里全面解释它们volatileinterface.com/…

标签: java jvm bytecode stack-frame


【解决方案1】:

Java 要求对所有加载的类进行验证,以维护沙箱的安全性并确保代码可以安全优化。请注意,这是在字节码级别完成的,因此验证不验证 Java 语言的不变量,它仅根据字节码规则验证字节码是否有意义.

除其他事项外,字节码验证可确保指令格式正确,所有跳转都指向方法内的有效指令,并且所有指令都对正确类型的值进行操作。最后一个是堆栈映射的来源。

问题是字节码本身不包含明确的类型信息。类型是通过数据流分析隐式确定的。例如,iconst 指令创建一个整数值。如果将其存储在插槽 1 中,则该插槽现在有一个 int。如果控制流从其中存储浮点数的代码合并,则该插槽现在被认为具有无效类型,这意味着在覆盖它之前您无法对该值执行更多操作。

从历史上看,字节码验证器使用这些数据流规则推断所有类型。不幸的是,不可能在一次线性遍历字节码中推断出所有类型,因为向后跳转可能会使已经推断出的类型无效。经典的验证器通过迭代代码来解决这个问题,直到一切都停止变化,可能需要多次通过。

但是,验证使 Java 中的类加载速度变慢。 Oracle 决定通过添加一个新的、更快的验证器来解决这个问题,该验证器可以一次验证字节码。为此,他们要求从 Java 7 开始的所有新类(Java 6 处于过渡状态)携带有关其类型的元数据,以便可以一次性验证字节码。由于字节码格式本身无法更改,因此此类型信息单独存储在名为StackMapTable 的属性中。

简单地在代码中的每个点存储每个值的类型显然会占用大量空间并且非常浪费。为了使元数据更小更高效,他们决定只列出作为跳转目标位置的类型。如果您考虑一下,这是您唯一需要额外信息来进行单次通过验证的时候。在跳转目标之间,所有控制流都是线性的,因此您可以使用旧的推理规则来推断中间位置的类型。

显式列出类型的每个位置称为堆栈映射框架。 StackMapTable 属性包含按顺序排列的帧列表,尽管它们通常表示为与前一帧的差异以减少数据大小。如果方法中没有帧,这种情况发生在控制流从不加入时(即 CFG 是一棵树),那么 StackMapTable 属性可以完全省略。

这就是 StackMapTable 工作原理以及添加它的原因的基本思路。最后一个问题是如何创建隐式初始帧。答案当然是在方法开始的时候,操作数栈是空的,局部变量slots有方法参数的类型给定的类型,这些类型是由方法描述符决定的。

如果您习惯于 Java,那么方法参数类型在字节码级别的工作方式会有一些细微差别。首先,虚方法有一个隐含的this 作为第一个参数。其次,boolean、byte、char和short在字节码级别不存在。相反,它们都在幕后作为整数实现。

【讨论】:

  • 作为对最后一段的修正,long 和 double 参数将与所有局部变量一样,在堆栈帧中消耗 两个 局部变量。跨度>
  • 一个晦涩的话题的精彩解释。
  • 我对这个字节码的东西还很陌生,但是如果我正在编写一个具有固定变量列表的应用程序,那么是否有必要为跳转定义帧?我的 ASM Eclipse 插件插入了帧,但似乎没有它们代码工作得很好——而且程序同时使用了 If 和 do-while。
  • 确实是新的,我不知道 ASM 可以配置为自动插入帧!
  • @ThomasRS 是的,ASM 可以自动计算帧数(虽然在很多情况下它的成本是手动计算的两倍——但根据朋友的经验,手动计算帧数很痛苦)
猜你喜欢
  • 2012-04-21
  • 2014-08-09
  • 1970-01-01
  • 2011-04-11
  • 2016-03-01
  • 2010-11-09
  • 2010-10-14
  • 2011-01-20
  • 2016-01-29
相关资源
最近更新 更多