今天我的一位朋友告诉我,如果一条指令调用虚拟它总是动态绑定,而invoke special总是静态绑定?这是真的吗?
如果是,那么为什么带有final关键字的方法也会调用虚拟?
请澄清我有这个疑问
答案 0 :(得分:8)
从本质上讲,是的,你是对的。有趣的部分是热点的介绍。 Hotspot本质上是另一个编译器,它知道当前加载了哪些类,甚至更加惊人地知道何时加载新类并且可以动态地使用更新的知识重新编译。 Hotspot可以选择使用javac不具备的知识来执行删除动态分派的优化。添加Hotspot后,javac中的代码被简化了。 Sun剥离了很多逻辑,主要是优化部分。
所以为了回答你的问题,javac并没有对方法的最终关键词给予大量关注。它顺从其更大的兄弟Hotspot。
Oracle在此处记录了Hotspot执行的一些优化:https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques。请查看方法部分。
答案 1 :(得分:2)
这比你朋友告诉你的要复杂得多。
invokespecial
指令始终提供静态(非分派)绑定。
invoke
指令提供了动态绑定,只要你能告诉。如果您使用HotSpot字节码解释器运行代码(例如,由-Xint
强制执行),那么我希望始终调度该调用。
但是,HotSpot编译器/优化器能够检测何时不需要调度。例如,HotSpot知道当前加载的所有类。如果将invoke
应用于未在任何已加载类中重写的方法,则可以优化调度并执行直接调用。此外,HotSpot足够智能,如果随后加载了另一个类,使优化器无效,则无法撤消此类优化"没有覆盖"假设
...为什么带有final关键字的方法也会调用虚拟?
由于final
方法的二进制兼容性规则,必要:
"将声明为final的方法更改为不再声明为final是不会破坏与预先存在的二进制文件的兼容性。" (JLS 13.4.7)
假设实例方法Foo.a()
声明为final
,Bar
中的一些代码用于调用该方法。如果字节码编译器对调用使用invokespecial
指令,那么如果我们将Foo.a()
更改为final
并且还为Foo
添加了子类。假设我们没有重新编译Bar
。我们最终可能会使用Bar
中使用invokespecial
的代码,即使调度是必要的。换句话说,我们已经破坏了Bar
对Foo.a()
的调用的二进制兼容性。
但是,正如我上面所解释的,优化器无论如何都可以处理这个问题,因为它可以根据全局分析确定是否需要在任何呼叫站点进行调度。