我已经在(Java)字节码上工作了一段时间了,但是,我从来没有想过为什么要输入一些指令?我理解在ADD操作中,我们需要区分整数加法和FP加法(这就是为什么我们有IADD和FADD)。然而,为什么我们需要区分ISTORE和FSTORE呢?它们都涉及完全相同的操作,即将32位从堆栈移动到局部变量位置?
我能想到的唯一答案是类型安全,以防止这种情况:(ILOAD,ILOAD,FADD)。然而,我相信类型安全已经在Java语言级别上得到了实施。好的,类文件格式不是直接与Java耦合的,所以对于不支持它的语言来说,这是一种强制类型安全的方法吗?有什么想法吗?谢谢。
编辑:跟进Reedy的答案。我写了一个最小的程序:
public static void main(String args[])
{
int x = 1;
}汇编如下:
iconst_1
istore_1
return使用字节码编辑器,我更改了第二条指令:
iconst_1
fstore_1
return它返回了一个java.lang.VerifyError:期望在堆栈上找到浮动。
我想知道,如果堆栈上没有关于类型的信息,只有位信息,那么FSTORE指令是如何知道它处理的是int而不是浮点的呢?
注意:,我找不到更好的标题来回答这个问题。你可以随意改进它。
发布于 2010-04-14 14:23:56
输入这些指令是为了确保程序是类型错误的。加载类时,虚拟机对字节码执行验证,以确保浮点数不作为参数传递给需要整数的方法。这种静态验证要求验证器可以确定堆栈上任何给定执行路径的类型和值的数目。加载和存储指令需要类型标记,因为堆栈帧中的局部变量不是类型化的(也就是说,您可以将istore存储到局部变量,然后再存储到相同的位置)。指令上的类型标记允许验证器知道在每个局部变量中存储的值类型。
验证器查看方法中的每个操作码,并在执行每个操作变量后跟踪堆栈和局部变量中的类型。您是对的,这是另一种类型检查形式,并且重复了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案例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给出错误是因为验证器知道fstore_1期望堆栈上有一个浮动,但是执行前面的指令的结果在堆栈上留下一个int。
这个验证是在不执行操作码的情况下完成的,而是通过查看指令的类型来完成的,就像java编译器在编写(Integer)"abcd"时会出现错误一样。编译器不需要运行程序就可以知道"abcd"是一个字符串,不能转换为Integer。
发布于 2010-04-14 14:50:05
要回答您的第一个问题,最好的猜测是:这些字节码是不同的,因为它们可能需要不同的实现。例如,特定的体系结构可能在主堆栈上保留整数操作数,但在硬件寄存器中保持浮点操作数。
为了回答第二个问题,在加载类时抛出VerifyError,而不是在执行该类时抛出它。验证过程被描述为这里;注释传递#3。
发布于 2012-06-16 01:20:32
如上文所述,所有字节码都必须是典型的静态数据流分析。但是,这并不能真正解释为什么像_store这样的指令有不同的类型,因为可以从堆栈上值的类型推断类型。实际上,有一些指令,如pop、dup和swap,正是这样做的,并对多个类型进行操作。为什么有些指令是输入的,而另一些则不是,这只能由Java的原始开发人员来解释。
https://stackoverflow.com/questions/2637879
复制相似问题