java 什么是堆栈映射框架

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25109942/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 07:21:49  来源:igfitidea点击:

What is a stack map frame

javajvmbytecodestack-frame

提问by Steven

I've recently been looking at The Java Virtual Machine Specifications(JVMS) to try to better understand the what makes my programs work, but I've found a section that I'm not quite getting...

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

Section 4.7.4describes the StackMapTableAttribute, and in that section the document goes into details about stack map frames. The issue is that it's a little wordy and I learn best by example; not by reading.

4.7.4介绍了StackMapTable属性,并在该节中的文件进入有关堆栈帧的地图细节。问题是它有点罗嗦,我最好通过例子学习;不是通过阅读。

I understand that the first stack map frame is derived from the method descriptor, but I don't understand how (which is supposedly explained here.) Also, I don't entirely understand what the stack map frames do. I would assume they're similar to blocks in Java, but it appears as though you can't have stack map frames inside each other.

我知道第一个堆栈映射帧是从方法描述符派生的,但我不明白如何(据说在这里解释)。另外,我不完全理解堆栈映射帧的作用。我认为它们类似于 Java 中的块,但看起来好像您不能在彼此内部使用堆栈映射框架。

Anyway, I have two specific questions:

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

  • What do the stack map frames do?
  • How is the first stack map frame created?
  • 堆栈映射帧有什么作用?
  • 第一个堆栈映射帧是如何创建的?

and one general question:

和一个一般性问题:

  • Can someone provide an explanation less wordy and easier to understand than the one given in the JVMS?
  • 有人能提供一个比 JVMS 中给出的解释更简洁、更容易理解的解释吗?

回答by Antimony

Java requires all classes that are loaded to be verified, in order to maintain the security of the sandbox and ensure that the code is safe to optimize. Note that this is done on the bytecode level, so the verification does notverify invariants of the Java language, it merely verifies that the bytecode makes sense according to the rules for bytecode.

Java 要求所有加载的类都经过验证,以维护沙箱的安全性并确保代码可以安全优化。注意,这是在字节码级别上完成的,所以验证并不会验证了Java的不变量的语言,它只是验证字节码,根据字节码的规则是有道理的。

Among other things, bytecode verification makes sure that instructions are well formed, that all the jumps are to valid instructions within the method, and that all instructions operate on values of the correct type. The last one is where the stack map comes in.

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

The thing is that bytecode by itself contains no explicit type information. Types are determined implicitly through dataflow analysis. For example, an iconst instruction creates an integer value. If you store it in slot 1, that slot now has an int. If control flow merges from code which stores a float there instead, the slot is now considered to have invalid type, meaning that you can't do anything more with that value until overwriting it.

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

Historically, the bytecode verifier inferred all the types using these dataflow rules. Unfortunately, it is impossible to infer all the types in a single linear pass through the bytecode because a backwards jump might invalidate already inferred types. The classic verifier solved this by iterating through the code until everything stopped changing, potentially requiring multiple passes.

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

However, verification makes class loading slow in Java. Oracle decided to solve this issue by adding a new, faster verifier, that can verify bytecode in a single pass. To do this, they required all new classes starting in Java 7(with Java 6 in a transitional state) to carry metadata about their types, so that the bytecode can be verified in a single pass. Since the bytecode format itself can't be changed, this type information is stored seperately in an attribute called StackMapTable.

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

Simply storing the type for every single value at every single point in the code would obviously take up a lot of space and be very wasteful. In order to make the metadata smaller and more efficient, they decided to have it only list the types at positions which are targets of jumps. If you think about it, this is the only time you need the extra information to do a single pass verification. In between jump targets, all control flow is linear, so you can infer the types at in between positions using the old inference rules.

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

Each position where types are explicitly listed is known as a stack map frame. The StackMapTableattribute contains a list of frames in order, though they are usually expressed as a difference from the previous frame in order to reduce data size. If there are no frames in the method, which occurs when control flow never joins (i.e. the CFG is a tree), then the StackMapTable attribute can be omitted entirely.

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

So this is the basic idea of how StackMapTable works and why it was added. The last question is how the implicit initial frame is created. The answer of course is that at the beginning of the method, the operand stack is empty and the local variable slots have the types given by the types of the method parameters, which are determined from the method decriptor.

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

If you're used to Java, there are a few minor differences to how method parameter types work at the bytecode level. First off, virtual methods have an implicit thisas first parameter. Second, boolean, byte, char, and shortdo not exist at the bytecode level. Instead, they are all implemented as ints behind the scenes.

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