Java jvm是否内联了最终的方法?
当我阅读java8规范时,我得到一个声明 在运行时,机器代码生成器或优化器可以“内联”最终方法的主体,用主体中的代码替换方法调用 所以我的问题是热点真的内联了最终的方法吗Java jvm是否内联了最终的方法?,java,jvm,hotspot,Java,Jvm,Hotspot,当我阅读java8规范时,我得到一个声明 在运行时,机器代码生成器或优化器可以“内联”最终方法的主体,用主体中的代码替换方法调用 所以我的问题是热点真的内联了最终的方法吗 或者,是否只有最后一个方法可以内联?比这复杂得多 Hotspot将尽其所能使事情快速运行。唯一的规则是:它不能破坏java规范提供的任何保证 这些保证没有提到“这将是内联的”之类的事情。除非您尝试使用nanoTime(),否则不可能观察到您正在内联,而且java规范显然没有对时间问题做出硬保证-在java规范中,无法观察到的
或者,是否只有最后一个方法可以内联?比这复杂得多 Hotspot将尽其所能使事情快速运行。唯一的规则是:它不能破坏java规范提供的任何保证 这些保证没有提到“这将是内联的”之类的事情。除非您尝试使用
nanoTime()
,否则不可能观察到您正在内联,而且java规范显然没有对时间问题做出硬保证-在java规范中,无法观察到的事情很少以某种方式得到保证。为什么要费心保证这些事情呢?这只会妨碍JVM工程师
当前流行的JVM实现使用这种粗略而过于简化的内联计划:
ClassLoader
的本机defineClass
方法中抛出一个新类),如果这导致任何假设不再为真,就会进行检查。如果发生这种情况,依赖于此假设的所有方法的热插销版本将失效,并将恢复到正常(缓慢,或多或少解释)操作。如果它们仍然大量运行,它们将再次被热插拔,这一次请记住,由于该方法实际上被重写,因此无法再轻松地内联
@jdk.internal.hospotintrinsicandidate
或@java.lang.invokeForceInline
这样的注释。这是不相关的,通常情况下,您不应该在编写代码时对内联的工作方式持有偏见。关键是你不需要知道
热点内联策略绝非微不足道。影响内联的因素很多 最重要的是方法的大小和“热度”,以及总内联深度。一个方法是否是最终的并不重要 HotSpot也可以轻松地内联虚拟方法。它甚至可以内联多态方法,如果频繁接收者不超过2个。有一篇文章详细描述了这种多模态调用是如何工作的 要分析特定情况下方法的内联方式,请使用以下诊断JVM选项:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
这将输出完整的编译树,其中包含每个方法的原因,以及它是否内联的原因:
java.util.regex.Pattern$Start::match (90 bytes)
@ 44 java.util.regex.Pattern$BmpCharProperty::match (55 bytes) inline (hot)
\-> TypeProfile (331146/331146 counts) = java/util/regex/Pattern$BmpCharProperty
@ 14 java.lang.String::charAt (25 bytes) inline (hot)
\-> TypeProfile (502732/502732 counts) = java/lang/String
@ 1 java.lang.String::isLatin1 (19 bytes) inline (hot)
@ 12 java.lang.StringLatin1::charAt (28 bytes) inline (hot)
@ 21 java.lang.StringUTF16::charAt (11 bytes) inline (hot)
@ 2 java.lang.StringUTF16::checkIndex (9 bytes) inline (hot)
@ 2 java.lang.StringUTF16::length (5 bytes) inline (hot)
@ 5 java.lang.String::checkIndex (46 bytes) inline (hot)
@ 7 java.lang.StringUTF16::getChar (60 bytes) (intrinsic)
@ 19 java.util.regex.Pattern$CharPredicate::is (0 bytes) virtual call
@ 36 java.util.regex.Pattern$Branch::match (66 bytes) inline (hot)
@ 36 java.util.regex.Pattern$GroupTail::match (111 bytes) inline (hot)
\-> TypeProfile (56997/278159 counts) = java/util/regex/Pattern$GroupTail
\-> TypeProfile (221162/278159 counts) = java/util/regex/Pattern$Branch
@ 70 java.util.regex.Pattern$BranchConn::match (11 bytes) inline (hot)
@ 70 java.util.regex.Pattern$LastNode::match (45 bytes) inline (hot)
\-> TypeProfile (56854/113708 counts) = java/util/regex/Pattern$LastNode
\-> TypeProfile (56854/113708 counts) = java/util/regex/Pattern$BranchConn
@ 7 java.util.regex.Pattern$Branch::match (66 bytes) inline (hot)
\-> TypeProfile (56598/56598 counts) = java/util/regex/Pattern$Branch
@ 32 java.util.regex.Pattern$Branch::match (66 bytes) inline (hot)
@ 32 java.util.regex.Pattern$GroupHead::match (47 bytes) already compiled into a big method
\-> TypeProfile (66852/267408 counts) = java/util/regex/Pattern$GroupHead
\-> TypeProfile (200556/267408 counts) = java/util/regex/Pattern$Branch
@ 32 java.util.regex.Pattern$Branch::match (66 bytes) recursive inlining is too deep
@ 32 java.util.regex.Pattern$GroupHead::match (47 bytes) already compiled into a big method
\-> TypeProfile (66852/267408 counts) = java/util/regex/Pattern$GroupHead
\-> TypeProfile (200556/267408 counts) = java/util/regex/Pattern$Branch
@ 50 java.util.regex.Pattern$GroupHead::match (47 bytes) already compiled into a big method
\-> TypeProfile (334260/334260 counts) = java/util/regex/Pattern$GroupHead
请仔细阅读javadoc。他们故意选了动词“can”。。。。你的问题没有通用的答案。非常感谢你的回答。我是JIT新手,虽然我的问题看起来有点愚蠢,但我只是对它的原理很好奇。附录:即使非最终方法被覆盖,JIT可以在一个特定的调用站点内联该方法,前提是该特定的调用站点仍然总是以原始方法结束,当然,当该假设不再成立时,会进行快速预检查,从而使该方法不再优化。因此,加载子类并不一定会使所有内联代码无效,它可能只会引入插入更多检查的必要性。子类通常只在某些地方使用,而大多数已经运行的代码不会改变其行为。