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你是说