两条指令都使用静态而不是动态调度。似乎唯一的实质性区别是invokespecial
始终将一个对象作为其第一个参数,该对象是分派方法所属类的实例。然而,invokespecial
实际上并没有把物体放在那里;编译器负责通过在发出之前发出适当的堆栈操作序列来实现这一点invokespecial
。所以更换invokespecial
with invokestatic
不应该影响运行时堆栈/堆的操作方式——尽管我预计它会导致VerifyError
因违反规范。
我很好奇制定两条基本上执行相同操作的不同指令背后的可能原因。我看了一下OpenJDK解释器的源码,好像是这样invokespecial
and invokestatic
处理方式几乎相同。拥有两条单独的指令是否有助于 JIT 编译器更好地优化代码,或者是否有助于类文件验证器更有效地证明某些安全属性?或者这只是 JVM 设计中的一个怪癖?
免责声明:很难确定,因为我从未阅读过有关此问题的明确 Oracle 声明,但我几乎认为原因如下:
当您查看 Java 字节码时,您可以对其他指令提出同样的问题。为什么验证者会在推两个时阻止你int
s 在堆栈上并将它们视为单个long
就在之后? (尝试一下,它会阻止你。)你可能会说,通过允许这样做,你可以用更小的指令集表达相同的逻辑。 (进一步来说,一个字节不能表达太多的指令,因此 Java 字节代码集应该尽可能地减少。)
当然,理论上你不需要字节码指令来推送int
s and long
s 到堆栈,你是对的,你不需要两条指令INVOKESPECIAL
and INVOKESTATIC
为了表达方法调用。方法由其唯一标识方法描述符(名称和原始参数类型)并且您无法在同一类中同时定义具有相同描述的静态和非静态方法。为了验证字节码,Java编译器必须检查目标方法是否是static
尽管如此。
Remark:这与v6ak的答案相矛盾。但是,非静态方法的方法描述符不会更改为包括对this.getClass()
。因此,Java 运行时始终可以从假设的方法描述符中推断出适当的方法绑定。INVOKESMART
操作说明。请参阅 JVMS §4.3.3。
理论就讲这么多。然而,两种调用类型所表达的意图却截然不同。请记住,Java 字节码应该由除 Java 之外的其他工具使用javac也可以创建 JVM 应用程序。通过字节码,这些工具生成的代码比 Java 源代码更类似于机器代码。但仍然是相当高的水平。例如,字节代码仍然被验证并且字节代码在编译为机器代码时被自动优化。然而,字节码是一种抽象,故意包含一些冗余,以便使meaning字节码更加明确。就像 Java 语言对相似的事物使用不同的名称以使语言更具可读性一样,字节码指令集也包含一些冗余。另一个好处是,验证和字节码解释/编译可以加速,因为方法的调用类型并不总是需要推断,而是在字节码中明确说明。这是可取的,因为验证、解释和编译都是在运行时完成的。
作为最后的轶事,我应该提到类的静态初始值设定项<clinit>
没有被标记static
在 Java 5 之前。在这种情况下,静态调用也可以通过方法名称来推断,但这会导致更多的运行时开销。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)