字节码中的类型

H-H*_*H-H 13 java verification bytecode

我已经在(Java)字节码上工作了一段时间,然而,我从未想过要问为什么会输入一些指令?我知道在ADD操作中,我们需要区分整数加法和FP加法(这就是为什么我们有IADD和FADD).但是,为什么我们需要区分ISTORE和FSTORE?它们都涉及完全相同的操作,即从堆栈移动32位到局部变量位置?

我能想到的唯一答案是类型安全,以防止这种情况:( ILOAD,ILOAD,FADD).但是,我认为类型安全性已经在Java语言级别实施.好的,Class文件格式没有直接与Java结合,所以这是一种为不支持它的语言强制执行类型安全的方法吗?任何想法?谢谢.

编辑:跟进Reedy的回答.我写了这个最小的程序:

public static void main(String args[])
{
    int x = 1;
}
Run Code Online (Sandbox Code Playgroud)

编译为:

iconst_1
istore_1
return
Run Code Online (Sandbox Code Playgroud)

使用字节码编辑器,我改变了第二条指令:

iconst_1
fstore_1
return
Run Code Online (Sandbox Code Playgroud)

它返回了一个java.lang.VerifyError:期望在堆栈上找到float.

我想知道,如果在堆栈上没有类型的信息,只有位,FSTORE指令是如何知道它处理的是int而不是浮点数?

注意:我找不到这个问题的更好的标题.随意改进它.

Geo*_*edy 18

键入这些指令以确保程序是类型安全的.加载类时,虚拟机会对字节码执行验证,以确保例如float不作为参数传递给期望整数的方法.此静态验证要求验证程序可以确定任何给定执行路径的堆栈上的值的类型和数量.加载和存储指令需要类型标记,因为堆栈帧中的局部变量不是键入的(即,您可以使用istore到局部变量,然后fstore到同一位置).指令上的类型标记允许验证者知道每个局部变量中存储的值的类型.

验证程序查看方法中的每个操作码,并在执行每个操作符后跟踪堆栈和局部变量中的类型.你是对的,这是另一种类型检查形式,并且复制了java编译器完成的一些检查.验证步骤可防止加载任何会导致VM执行非法指令的代码,并确保Java平台的安全属性,而不会在每次操作之前导致检查类型的大运行时间损失.每次执行方法时,每个操作码的运行时类型检查都会受到影响,但静态验证只在加载类时执行一次.

情况1:

Instruction             Verification    Stack Types            Local Variable Types 
----------------------- --------------- ---------------------- ----------------------- 
<method entry>          OK              []                     1: none
iconst_1                OK              [int]                  1: none
istore_1                OK              []                     1: int
return                  OK              []                     1: int
Run Code Online (Sandbox Code Playgroud)

案例2:

Instruction             Verification    Stack Types            Local Variable Types 
----------------------- --------------- ---------------------- ----------------------- 
<method entry>          OK              []                     1: none
iconst_1                OK              [int]                  1: none
fstore_1                Error: Expecting to find float on stack
Run Code Online (Sandbox Code Playgroud)

给出错误是因为验证程序知道fstore_1要求堆栈上有浮点数,但执行前面指令的结果会在堆栈上留下一个int.

这个验证是在不执行操作码的情况下完成的,而是通过查看指令的类型来完成,就像java编译器在编写时给出错误一样(Integer)"abcd".编译器不必运行程序就知道这"abcd"是一个字符串而无法转换为Integer.


Chr*_*rau 5

Geoff Reedy 在他的回答中解释了加载类时验证器会做什么。我只是想补充一点,您可以使用 JVM 参数禁用验证程序。不推荐这样做!

对于您的示例程序(带有 icont 和 fstore),在禁用验证的情况下运行的结果是一个 VM 错误,该错误会停止 JVM 并显示以下消息:

=============== DEBUG MESSAGE: illegal bytecode sequence - method not verified ================

#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_PRIV_INSTRUCTION (0xc0000096) at pc=0x00a82571, pid=2496, tid=3408
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_15-b04 mixed mode, sharing)
# Problematic frame:
# j  BytecodeMismatch.main([Ljava/lang/String;)V+0
#
...
Run Code Online (Sandbox Code Playgroud)