写在前面
本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和文献引用请见100个问题搞定Java虚拟机
解答
方法内联是指,在编译过程中,当遇到方法调用时,将目标方法的方法体纳入编译范围之中,并取代原方法调用的优化手段。
Java 虚拟机中的即时编译器会使用内联缓存来加速动态绑定。
Java 虚拟机所采用的单态内联缓存将纪录调用者的动态类型,以及它所对应的目标方法。
当碰到新的调用者时,如果其动态类型与缓存中的类型匹配,则直接调用缓存的目标方法。
否则,Java 虚拟机将该内联缓存劣化为超多态内联缓存,在今后的执行过程中直接使用方法表进行动态绑定。
- 1
- 2
- 3
- 4
- 5
补充
内联缓存
内联缓存是一种加快动态绑定的优化技术。它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。
在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法。如果没有碰到已缓存的类型,内联缓存则会退化至使用基于方法表的动态绑定。
多态优化
单态内联缓存
单态(monomorphic)指的是仅有一种状态的情况。
单态内联缓存即只缓存了一种动态类型以及它所对应的目标方法。
实现原理
比较所缓存的动态类型,如果命中,则直接调用对应的目标方法。
多态内联缓存
多态(polymorphic)指的是有限数量种状态的情况。二态(bimorphic)是多态的其中一种。
多态内联缓存即缓存了多个动态类型及其目标方法。
实现原理
它需要逐个将所缓存的动态类型与当前动态类型进行比较,如果命中,则调用对应的目标方法。
一般来说,我们会将更加热门的动态类型放在前面。
超多态
超多态指的是更多种状态的情况。通常我们用一个具体数值来区分多态和超多态。在这个数值之下,我们称之为多态。否则,我们称之为超多态。
JVM 与单态内联缓存
在实践中,大部分的虚方法调用均是单态的,也就是只有一种动态类型。为了节省内存空间,Java 虚拟机只采用单态内联缓存。
内联缓存没有命中 JVM 怎么处理?
当内联缓存没有命中的情况下,Java 虚拟机需要重新使用方法表进行动态绑定。
对于内联缓存中的内容,我们有两种选择。
一、替换
替换单态内联缓存中的纪录。
这种做法就好比 CPU 中的数据缓存,它对数据的局部性有要求,即在替换内联缓存之后的一段时间内,方法调用的调用者的动态类型应当保持一致,从而能够有效地利用内联缓存。
因此,在最坏情况下,我们用两种不同类型的调用者,轮流执行该方法调用,那么每次进行方法调用都将替换内联缓存。
也就是说,只有写缓存的额外开销,而没有用缓存的性能提升。
二、劣化
劣化为超多态状态。这也是 Java 虚拟机的具体实现方式。
处于这种状态下的内联缓存,实际上放弃了优化的机会。
它将直接访问方法表,来动态绑定目标方法。与替换内联缓存纪录的做法相比,它牺牲了优化的机会,但是节省了写缓存的额外开销。