如何解决jvm是否可以内联最终方法?
当我阅读java8规范时,得到的声明是
在运行时,机器代码生成器或优化器可以“内联”最终方法的主体,用其主体中的代码替换该方法的调用。
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.3.3
所以我的问题是,热点是否真正内联了最终方法?
或者,是否只能内联最终方法?
解决方法
HotSpot内联策略绝非易事。有许多factors and heuristics会影响内联。
最重要的是方法的大小和“热度”以及总的内联深度。一个方法是否为最终方法并不重要。
HotSpot也可以轻松地内联虚拟方法。如果不超过两个频繁接收者,它甚至可以内联 polymorphic 方法。有epic post详细描述了这种多态调用的工作方式。
要分析在特定情况下如何内联方法,请使用以下诊断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
,
要复杂得多。
热点将尽一切必要使事情快速运行。唯一的规则是:它不能破坏Java规范提供的任何保证。
这些保证未提及“将被内联”之类的内容。除非您尝试使用nanoTime()
来解决问题,否则不可能观察到您正在内联,并且Java规范显然不对计时问题做出任何硬性保证-不可能做到的事情在Java规范中,很少保证观察到的观察结果是很少的。为什么要麻烦保证这些事情呢?它只会妨碍JVM工程师。
当前流行的JVM实现使用此粗略且过于简化的计划进行内联:
-
任何内在的方法(无论是否成功)都将被内联。
-
这永远不会立即发生。如果被选择用于重新编译热点方法X它只发生,且X调用方法Y,和Y似乎是一个精细的方式内嵌
-
如果内联非最终方法,那似乎是非法的举动。但是,事实并非如此:VM只是作了一个虚假的假设:“尽管这种方法不是最终的,但仍不会被VM中加载的任何类中的任何地方所覆盖。”如果目前正确,则可以内联该方法,即使以后可能不正确。
-
每次加载新类时(归结为:在
ClassLoader
的本机defineClass
方法上抛出了一个新类),将检查是否导致任何假设不再是真的。如果发生这种情况,则所有依赖此假设的方法的热点版本都会无效,并将恢复为正常(缓慢的,或多或少的解释)操作。如果仍然运行很多,它们将再次热点,这一次要记住,由于实际上已重写该方法,因此无法轻松对其进行内联。 -
正如我所说,这就是现在的工作方式。在将来的Java版本中,它可能会有所不同,因为不能保证所有这些。它也被简化了。例如,我没有提到
@jdk.internal.HotSpotIntrinsicCandidate
或@java.lang.invokeForceInline
之类的注释。这无关紧要,并且通常应该不要以内联的工作方式为前提编写代码。重点是您不需要知道。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。