这两个指令都使用静态而非动态分派.似乎唯一重要的区别在于invokespecial,作为第一个参数,它总是具有一个对象,该对象是调度方法所属的类的实例.但是,invokespecial实际上并没有将物体放在那里; 编译器负责通过在发射之前发出适当的堆栈操作序列来实现这一点invokespecial.因此,更换invokespecial与invokestatic不应该影响运行栈/堆被操纵的方式-虽然我预计这将导致VerifyError违反规范.
我很好奇制作两个截然不同的指令背后可能的原因.我看了一下OpenJDK解释器的来源,它似乎invokespecial和invokestatic处理几乎相同.有两个单独的指令有助于JIT编译器更好地优化代码,还是帮助类文件验证器更有效地证明某些安全属性?或者这只是JVM设计中的一个怪癖?
有以下定义:
\n\n存在显着差异。假设我们要设计一条指令,它将在和 之间invokesmart明智地进行选择:inkovestaticinvokespecial
首先,区分静态和虚拟调用不会有问题,因为我们不能有两个具有相同名称、相同参数类型和相同返回类型的方法,即使一个是静态的,第二个是虚拟的。JVM 不允许这样做(出于一个奇怪的原因)。感谢 raphw 注意到这一点。
\n\n首先,invokesmartfoo/Bar.baz(I)I意味着什么?这可能意味着:
foo.Bar.baz,从操作数堆栈中消耗int并添加另一个int.// (int) -> (int)foo.Bar.baz,消耗操作数堆栈中的 和 并foo.Bar添加。intint// (foo.Bar, int) -> (int)你会如何选择?两种方法都可能存在。
foo/Bar.baz(Lfoo/Bar;I)我们可以尝试通过要求静态调用来解决它。然而,我们可能同时拥有public static int baz(Bar, int)和 public int baz(int)。
我们可能会说这并不重要,并且可能会禁用这种情况。(我不认为这是一个好主意,只是想象一下。)这意味着什么?
\n\nACC_SUPER。因此,这意味着非常不一致的行为。
\n