我目前正在编写一个创建Java字节代码并编写方法调用的应用程序。到目前为止,编写此字节代码的模块没有关于调用方法调用的实例的实际类型的信息,但它确实知道为其定义特定方法的类型。例如:
class Foo {
public void foo() { }
}
class Bar extends Foo {
public void bar() { }
}
当前版本的引擎将执行
INVOKEVIRTUAL Foo.foo
即使是Bar
类型的对象,因为它知道foo
中已定义Foo
。这在JVM中是合法的(当然),但Java编译器会将其转换为
INVOKEVIRTUAL Bar.foo
当它是"正常" Java源代码。我目前想知道JVM是否实际使用了显式子类型的信息,或者在加载类时是否忽略/优化了它。我想知道,因为验证程序正在计算实际的类型,并且不会让我编写非法的方法调用,我想知道为什么运行时在它已经可用时不会使用这些信息。我特别想知道当超类型是接口(INVOKEINTERFACE
)时它是否会对性能产生影响,如果JVM无法确定实际类型,则无法使用虚方法表。
我当然可以扩展我的模块,但我需要提供额外的信息,这会使我的代码膨胀,如果它没有效果,我不想做。所以我问:类型影响性能还是JVM会处理这种明确的解决方案?
答案 0 :(得分:7)
字节码中的任何区别都不会对JIT编译代码的性能产生太大影响。
HotSpot会定期为每个invokevirtual
呼叫网站保留一个类型配置文件。如果记录表明只调度了一种类型,JIT编译器会将其视为invokespecial
调用,基本上是直接跳转到被调用者,甚至内联被调用者。
以上描述了最难的情况的优化,一种完全通用的虚拟方法。 HotSpot还知道哪些方法有效最终:在加载类的集合中,不会出现对该方法的覆盖。在这种情况下,HotSpot继续类似于上面的操作,但省略了一些指令(执行类型断言的指令)。
我还在努力破解.class文件,使invokevirtual
指令被invokespecial
替换。结果是VerifyError
:
Exception in thread "main" java.lang.VerifyError:
Bad invokespecial instruction: current class isn't assignable to reference class.
您只能将invokespecial
用于当前类或其祖先的方法,实际上这是specified by the Java Virtual Machine Specification, §4.9.2:
每个
invokespecial
指令必须命名实例初始化方法(第2.9节),当前类中的方法或当前类的超类中的方法。
答案 1 :(得分:2)
invokeinterface
比invokevirtual
/ invokestatic
慢,尤其是涉及所谓的“变形调用”时:方法有很多实现,而JIT -compiler无法优化。因此,首选invokevirtual
至invokeinterface
是明智之举。而且你提到的类型越具体,乐观优化就越有可能(我们认为这个方法是最终的,直到覆盖它的类被加载)才会有效。
此处提供了更多信息: http://java.dzone.com/articles/invoke-interface-optimisations