Java 什么是堆栈映射帧

Java 什么是堆栈映射帧,java,jvm,bytecode,stack-frame,Java,Jvm,Bytecode,Stack Frame,我最近一直在研究(JVM),试图更好地理解是什么让我的程序工作,但我发现了一个我不太了解的部分 第节介绍StackMapTable属性,在该节中,文档将详细介绍堆栈映射框。问题是它有点罗嗦,我通过例子学习得最好;不是通过阅读 我知道第一个堆栈映射帧是从方法描述符派生出来的,但我不知道如何(这应该已经解释过了)而且,我也不完全理解堆栈映射帧的作用。我假设它们类似于Java中的块,但看起来好像彼此之间不能有堆栈映射帧 无论如何,我有两个具体问题: 堆栈贴图帧的作用是什么 如何创建第一个堆栈贴图帧

我最近一直在研究(JVM),试图更好地理解是什么让我的程序工作,但我发现了一个我不太了解的部分

第节介绍StackMapTable属性,在该节中,文档将详细介绍堆栈映射框。问题是它有点罗嗦,我通过例子学习得最好;不是通过阅读

我知道第一个堆栈映射帧是从方法描述符派生出来的,但我不知道如何(这应该已经解释过了)而且,我也不完全理解堆栈映射帧的作用。我假设它们类似于Java中的块,但看起来好像彼此之间不能有堆栈映射帧

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

  • 堆栈贴图帧的作用是什么
  • 如何创建第一个堆栈贴图帧
还有一个一般性问题:

  • 有人能提供一个比JVM中给出的解释更简单、更容易理解的解释吗

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

除此之外,字节码验证确保指令格式良好,所有跳转都指向方法中的有效指令,并且所有指令都在正确类型的值上操作。最后一个是堆栈映射的位置

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

过去,字节码验证器使用这些数据流规则推断所有类型。不幸的是,不可能在通过字节码的单个线性过程中推断出所有类型,因为向后跳转可能会使已经推断出的类型无效。经典的验证器通过迭代代码来解决这个问题,直到所有内容都停止更改,这可能需要多次传递

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

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

明确列出类型的每个位置称为堆栈映射框。
StackMapTable
属性包含按顺序排列的帧列表,尽管它们通常表示为与前一帧的差异,以减少数据大小。如果方法中没有帧(当控制流从不连接(即CFG是一棵树)时发生),则可以完全忽略StackMapTable属性

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


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

@EJP这是我正在研究的东西。这是我决定首先阅读JVM的主要原因之一。@EJP我也一直在阅读JVM规范,相信我,这不仅仅是阅读规范,例如:要理解类型验证如何工作的部分(与此问题相关),您需要具备Prolog编程的基本知识。。。因此,我认为这方面的问题/答案值得一提Stackoverflow@morgano,我发现忽略Prolog的内容而专注于经典的推理验证器更有帮助。新的验证器非常相似,他们只是决定在200页的Prolog中指定它,而不是像旧的那样使用vauge英语描述one@Antimony确切地说,这就是这个问题的内容,将正式规范翻译成通俗易懂的英语。作为对你最后一段的修正,我在这里试图全面地解释它们,
long
double
参数将像所有局部变量一样,在堆栈框架中使用两个局部变量。这是一个晦涩难懂的主题的很好的解释。我对字节码这类东西很陌生,但如果我正在编写一个具有固定变量列表的应用程序