为什么有一个单独的字节码可以用来执行静态/虚拟/接口/特殊方法?

时间:2011-03-15 10:02:16

标签: java bytecode javac

由于每个方法调用都包含目标方法签名,在我看来,类验证步骤可以通过分析目标来判断它是否调用静态,虚拟等并做正确的事情?

4字节代码是浪费3字节代码还是仅仅是自我记录,如果使用javap转储类文件?

6 个答案:

答案 0 :(得分:3)

如果您正在谈论invokeinterfaceinvokespecialinvokestaticinvokevirtual,则 之间存在差异。开始时对堆栈的影响。

答案 1 :(得分:2)

假设您只调用虚拟和静态操作码。我们假设在编译时你有

Test {
        public static void test(Test t) {
        // static method
    }
}

在某处用伪字节码调用此静态

push t
jmp Test.test(T)V

现在让我们假设有人在运行时使用另一个Test类

Test {
    public void test(Test t) {
        // obj call
    }
}

在这种情况下,jmp Test.test(T)V仍然有效。问题是它会弄乱堆栈。两个不同的操作码允许捕获此问题的链接时间。

答案 2 :(得分:0)

这是可能的。但是,每次解释其中一条指令时,它会增加额外的成本。所以表现会受到很大的好处。

有人认为,单独的指令可以减少混淆VM的可能性,从而提高安全性。不确定我是否相信。

答案 3 :(得分:0)

您需要此操作码,因为在布置方法的字节码引用时,它们仅包含对常量池中条目的符号引用。要解析这些引用,您需要知道需要解析的类型:例如,invokespecial和invokevirtual之间的区别是静态绑定和动态绑定。对于invokespecial,您可以解析引用的类型,而不是对象的类。如果没有编译器输入特殊操作码来区分方法,则无法收集此信息。在java中,代码通常是动态链接的,这就是强制这些操作码的原因。

答案 4 :(得分:0)

至少需要invokespecial作为单独的指令,因为java允许显式调用重写方法的超级实现:

class Super {
    public void m() { }
}
class Sub {
    public void m() {
        super.m();
    }
}

Sub中的来电必须是invokespecial,否则只会自行调用并耗尽堆栈。

答案 5 :(得分:0)

我认为这场辩论是不必要的,因为java有这个设计决定。不确定,但您可能希望再查看一下他们称之为用户定义的数据端点:invokedynamic