Java 为什么有4个单独的字节码用于执行静态/虚拟/接口/特殊方法,而一个字节码就足够了?

Java 为什么有4个单独的字节码用于执行静态/虚拟/接口/特殊方法,而一个字节码就足够了?,java,bytecode,javac,Java,Bytecode,Javac,因为每个方法调用都包含目标方法签名,所以在我看来,类验证步骤可以通过分析目标来判断它是否调用了静态、虚拟等,并且做了正确的事情 使用4字节代码是浪费3字节代码,还是如果使用javap转储类文件,这仅仅是自我记录 如果您谈论的是invokeinterface、invokespecial、invokestatic和invokevirtual,那么它们之间是有区别的。首先是对堆栈的影响。如果您谈论的是invokeinterface、invokespecial、invokestatic和invokevi

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


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

如果您谈论的是
invokeinterface
invokespecial
invokestatic
invokevirtual
,那么它们之间是有区别的。首先是对堆栈的影响。

如果您谈论的是
invokeinterface
invokespecial
invokestatic
invokevirtual
,它们之间存在差异。开始时对堆栈的影响。

这是可能的。但是,每次解释其中一条指令时都会增加额外的成本。因此,性能将因很少的优势而受到损害


有人认为,单独的指令通过减少混淆VM的机会来提高安全性。不确定我是否相信这一点。

这是可能的。但是,每次解释其中一条指令时都会增加额外的成本。因此,性能将因很少的优势而受到损害


有人认为,单独的指令通过减少混淆VM的机会来提高安全性。不确定我是否相信这一点。

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

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

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

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

Sub
中的调用必须是一个
invokespecial
,否则它只会调用自身并耗尽堆栈。

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

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

Sub
中的调用必须是一个
invokespecial
,否则它只会调用自身并耗尽堆栈。

假设您只有虚拟和静态的invoke操作码。让我们假设在编译时

Test {
        public static void test(Test t) {
        // static method
    }
}
然后在某个地方用伪字节码调用这个static

push t
jmp Test.test(T)V
现在让我们假设有人在运行时使用另一个测试类

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

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

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

Test {
        public static void test(Test t) {
        // static method
    }
}
然后在某个地方用伪字节码调用这个static

push t
jmp Test.test(T)V
现在让我们假设有人在运行时使用另一个测试类

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

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

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

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

虚拟机永远不会混淆,因为它总是在目标现在是接口(需要调用接口)时验证并抱怨代码是否具有调用虚拟。当然,如果您更新第三方jar,并且目标从接口类等更改,则可能会发生这种情况。@mP原因是组合指令会增加漏洞的风险。什么漏洞?加载类时,运行时知道目标,并验证指令是否与目标匹配。如果调用ins的方法对于目标是错误的,那么您将得到一个验证负载error@mP潜在的弱点。你是说漏洞是不可能的吗?即使对于堆栈溢出,我也不觉得这特别聪明。VM从不感到困惑,因为当目标现在是接口(需要调用接口)时,它总是验证并抱怨代码是否具有调用虚拟。当然,如果您更新第三方jar,并且目标从接口类等更改,则可能会发生这种情况。@mP原因是组合指令会增加漏洞的风险。什么漏洞?加载类时,运行时知道目标,并验证指令是否与目标匹配。如果调用ins的方法对于目标是错误的,那么您将得到一个验证负载error@mP潜在的弱点。他们发生了。@mP你是说