是什么让JVM的最新版本更快?

Sou*_*nta 16 java performance jvm scala native-code

我最近看到多个声明,谈论Java(以及基于JVM的语言,如Scala)在性能上与C/C++代码的可比性.

例如,从ScalaLab项目的描述:

基于Scala的脚本编写速度,接近本机和优化Java代码的速度,因此与基于C/C++的科学代码接近甚至更好!

有人能指出我对这些JVM优化的概述吗?是否有任何真正的基准支持这一主张或提供一些真实的比较?

Bee*_*ope 22

表演技巧

首先,这取决于哪个 JVM你所谈论的,因为有几个-但我会假设你的意思甲骨文热点(在任何情况下,其他顶级的JVM将使用类似的技术).

对于那个JVM,来自HotSpot内部wiki的这个列表提供了一个很好的开始(并且子页面详细介绍了一些更有趣的技术).如果你只是在寻找一个技巧的清单,维基也有这个,虽然为了理解它们你可能不得不谷歌个人条款.

并非所有这些都是最近实现的,但是一些大的已经实现(范围检查省略,逃逸分析,超级词优化) - 至少对于"最近"的松散定义.

接下来让我们看一下C/C++与Java相关的性能图,以及为什么上述技术有助于缩小差距,或者在某些情况下实际上提供Java和本机编译语言的内在优势.

Java与C/C++

从较高的层面来看,优化是你在任何体面的编译器中看到的对C和C++等本地语言的混合,以及减少Java/JVM特定功能和安全检查的影响所需要的东西.如:

  • 转义分析可以减轻(某种程度上)对象的无堆栈分配
  • 内联缓存和类层次结构分析,可缓解"每个功能都是虚拟的"
  • 范围检查消除,减轻"每个阵列访问范围检查"

许多这些特定于JVM的*优化仅有助于使JVM与本地语言保持一致,因为它们正在解决本地语言无需处理的障碍.然而,一些优化是静态编译的语言无法管理的事情(或者在某些情况下只能通过配置文件引导的optmization进行管理,这种情况很少见且无论如何都必须适合所有人):

  • 仅动态内联最热门的代码
  • 基于实际分支/交换频率的代码生成
  • 动态生成CPU /指令集识别代码(甚至编译代码后发布的CPU功能!)1
  • 删除从未执行过的代码
  • 注入与应用程序代码交错的预取指令
  • 安全点支持的全系列技术

共识似乎是Java经常生成与中等优化级别的良好C++编译器速度相似的代码,例如gcc -O2,尽管很大程度上取决于确切的基准.像HotSpot这样的现代JVM倾向于在低级别数组遍历和数学方面表现优异(只要竞争编译器没有向量化 - 这很难被击败),或者在竞争代码执行相似数量的分配时具有大量对象分配的情况下(JVM对象分配+ GC通常比malloc更快),但是当典型Java应用程序的内存损失是一个因素,其中堆栈分配被大量使用,或者向量化编译器或内在函数将缩放转向本机代码时,它会下降.

如果你搜索Java vs C性能,你会发现很多人已经解决了这个问题,并且具有不同程度的严谨性.这是我偶然发现第一个,它似乎显示了gcc和HotSpot之间的粗略联系(在这种情况下甚至在-O3).如果您想了解单个基准测试如何在每种语言中进行多次迭代,相互跨越,并且显示双方优化的一些限制,那么这篇文章和链接的讨论可能是一个更好的开始.

*确实不是特定于JVM的 - 大多数也适用于其他安全或托管语言,如CLR


1随着新指令集(特别是SIMD指令,但还有其他指令)以某种频率发布,这种特殊优化变得越来越重要.自动矢量化可以大规模地加速某些代码,虽然Java在这里已经慢慢消失,但它们至少会有所收获.


Ant*_*ony 13

实际表现当然取决于基准,因应用而异.但很容易看出JIT VM如何与静态编译代码一样快,至少在理论上如此.

JIT代码的主要优势在于它可以根据仅在运行时知道的信息进行优化.在C链接DLL时,您必须每次都进行该函数调用.在动态语言中,函数可以内联,即使它是在运行时加载的函数,这要归功于及时编译.

另一个例子是基于运行时值进行优化.在C/C++中,您使用预处理器宏来禁用断言,如果要更改此选项,则必须重新编译.在Java中,通过设置私有布尔字段然后在代码中放置if分支来处理断言.但是,由于VM可以根据标志的值编译包含或不包含断言代码的代码版本,因此几乎没有性能损失.

另一个主要的VM创新是多态内联.Idomatic Java非常注重小型包装器方法,如getter和setter.为了获得良好的性能,内联它们显然是必要的.在实际只调用一种类型的常见情况下,VM内联多态函数不仅可以通过包含具有适当代码的内联缓存来内联调用多种不同类型的代码.如果代码开始在许多不同类型上运行,则VM可以检测到这一点并回退到较慢的虚拟分派.

静态编译器当然不能做到这一点.强大的静态分析只能让你到目前为止.这不仅限于Java,尽管这是最明显的例子.Google的V8 vm for Javascript也非常快.Pypy旨在为Python和Rubinius为Ruby做同样的事情,但它们并不完全存在(当你有一家大公司支持你时它会有所帮助).