Jvm:编译器需要在方法字节码中的哪些位置指定堆栈映射帧?



这是asm用户指南的摘录:

为了节省空间,编译方法 每条指令不包含一个帧:实际上它只包含帧 对于与跳转目标或异常处理程序对应的指令,或 遵循无条件跳转指令。事实上,其他帧可以是 从这些中轻松快速地推断出来。

我可以理解为什么 jvm 在跳转目标和异常处理程序需要堆栈映射帧,但goto之后不需要堆栈映射帧是不必要的要求,因为方法字节码中的某个地方必须有一个跳转指令,它指向goto指令之后的指令, 该案件将由第一个要求处理。它必须是这样,否则goto之后的指令将无法访问,因此可以丢弃。

例:

下面给出了一个方法及其字节码:

public class t {
public static void main(String[] s) {
int i = 10;
while ( i > 0 ) {
i = i + 1;
}
int j = 10;
}
}

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: bipush        10
2: istore_1
3: iload_1
4: ifle          14
7: iload_1
8: iconst_1
9: iadd
10: istore_1
11: goto          3
14: bipush        10
16: istore_2
17: return
LineNumberTable:
line 9: 0
line 10: 3
line 11: 7
line 13: 14
line 14: 17
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 3
locals = [ int ]
frame_type = 10 /* same */
}

索引11处的指令是 goto 指令,索引4处的指令有14,该指令刚好在11之后作为跳转目标。

在无条件跳转后需要堆栈地图框背后的理由是什么?

堆栈映射的目标是允许在代码的单个线性传递中执行验证。

如果 goto 后面的代码没有指向它的跳转,那么代码就是死的,理论上不需要验证。但是,验证者不知道这一点。由于它正在执行代码的单次传递,因此不可能提前知道代码是否失效。因此,它必须验证所有内容,这意味着它需要一个堆栈帧来开始。

请注意,这与旧的非堆栈映射验证程序不同。在引入堆栈映射之前,验证器将简单地遍历它找到的所有代码,(有效地(在跳转之后,并迭代直到收敛。这意味着在旧的验证器下,死代码永远不会被触及。

相关内容

最新更新