这两个指令都使用静态而非动态调度。 这似乎是唯一的实质性区别在于invokespecial
永远有,作为第一个参数,一个对象,它是类的一个实例是,派出方法属于。 然而, invokespecial
实际上并不把对象那里。 编译器是一个负责使通过发射之前发射的堆栈操作的适当序列发生invokespecial
。 所以更换invokespecial
与invokestatic
应该不会影响运行栈/堆被操纵的方式-尽管我期望它会造成VerifyError
违反规范。
我很好奇背后做的是基本上做同样的事情,两个不同的指令可能的原因。 我看了看在OpenJDK解释的来源,它似乎像invokespecial
和invokestatic
几乎相同的处理。 是否有两个单独的指令帮助JIT编译器更好地优化代码,或者它帮助类文件验证器更有效地证明了一些安全性? 或者这只是在JVM的设计怪癖?
有定义:
- http://docs.oracle.com/javase/specs/jvms/se5.0/html/Instructions2.doc6.html#invokestatic
- http://docs.oracle.com/javase/specs/jvms/se5.0/html/Instructions2.doc6.html#invokespecial
有显著差异。 说,我们要设计一个invokesmart
指令,这之间巧妙地选择inkovestatic
和invokespecial
:
首先,它不会是静态的,虚拟呼叫区分一个问题,因为我们不能有两个方法有相同的名字,相同的参数类型和相同的返回类型,即使一个是静态的,第二个是虚拟的。 JVM不允许(一个奇怪的原因)。 感谢raphw用于注意到,。
首先,你会invokesmart foo/Bar.baz(I)I
的意思吗? 这可能意味着:
静态方法调用foo.Bar.baz
消耗int
从操作数堆栈,并增加了一个int
。 // (int) -> (int)
实例的方法调用foo.Bar.baz
消耗foo.Bar
和int
从操作数堆栈,并增加了int
。 // (foo.Bar, int) -> (int)
你会如何从中选择呢? 可能存在两种方法。
我们不妨通过要求来解决它foo/Bar.baz(Lfoo/Bar;I)
的静态调用。 然而,我们可能有两个public static int baz(Bar, int)
和public int baz(int)
我们可以说,不要紧,可能禁用此类情况。 (我不认为这是一个好主意,但只是想象。)会是什么意思?
- 如果该方法是静态的,可能不会有额外的限制。 在另一方面,如果该方法不是静态的,也有一些限制:“最后,如果解决方法是受保护(§4.6),它要么是当前类的成员或当前的超类中的一员类,那么类objectref的必须是当前类或当前类的子类。”
- 有一些进一步的差异,请参阅有关的说明
ACC_SUPER
。 - 这将意味着所有被引用的类必须字节码验证之前加载。 我希望现在这是没有必要的,但我不是100%肯定。
因此,这将意味着非常不一致的行为。
免责声明:这是很难说肯定的,因为我从来没有读过关于这个明确的Oracle的声明,但我几乎认为这是什么原因:
当你在Java字节码,你可以询问其他指令同样的问题。 为什么要验证阻止你推二:当int
堆栈上的S和对待他们作为一个单一的long
之后? (试试吧,它会阻止你。)你可以争辩说,允许这一点,你可以用更小的指令集表达了同样的逻辑。 (要使用这种说法走得更远,一个字节无法表达太多的指示,Java字节代码集,因此应尽可能降低。)
当然,从理论上讲,你不需要为推动字节码指令int
S和long
s到堆栈,你是对的事实,你会不会需要两个指令INVOKESPECIAL
和INVOKESTATIC
为了表达方法调用。 一种方法,通过它的方法描述符 (名称和原始参数类型)来标识,你不能同时定义的静态和相同类内的相同描述的非静态方法。 而为了验证字节代码,Java编译器必须检查目标方法是否是static
还是。
备注 :此矛盾v6ak的答案。 然而,非静态方法的方法描述符是不改变,以包括到参考this.getClass()
因此,Java运行时总是可以推断适当的方法从方法描述为一个虚构的结合INVOKESMART
指令。 见JVMS§4.3.3。
这么多的理论。 然而,由这两种类型的调用表达的意图有很大的不同。 请记住,Java字节代码应该比javac的其他工具可以用来创建JVM的应用,以及。 随着字节码,这些工具产生的东西,比你的Java源代码更类似于机器代码。 但它仍然是相当高的水平。 例如,字节代码仍然被验证,并且当编译成机器代码的字节代码被自动优化。 但是,字节码是故意包含为了使字节代码的含义更加明确一些冗余的抽象。 而就像Java语言使用了类似的事情不同的名称,以使语言更具可读性,字节码指令集包含一定的冗余度为好。 而作为另一个好处,验证以及字节码解释/编译就可以来一个方法的调用类型并不总是需要被推断,但在字节码是明确指出加快。 因为验证,解释和汇编,在运行时完成这是可取的。
作为最后一个故事,我应该指出,一个类的静态初始化<clinit>
未标记static
的Java 5之前在此背景下,静态调用也可以通过该方法的名字推断,但这会造成更大的运行时开销。
为了得到关于这些代码的一个明确的实际想法,你需要添加的Eclipse插件ASM在Eclipse IDE中,并找出为其创建了您的样本Hello World程序生成的字节码。