为什么字节码可能比本机代码运行得更快

Java很慢。

这不仅仅是一个“都市传奇”,它似乎是一个事实。 由于延迟,您不会将其用于实时编码,也不会将其用于群集/并行计算。 有数以千计的基准测试,特别是“Java vs C#vs C ++”。

http://benchmarksgame.alioth.debian.org/

根据上面的网站,不仅Java性能几乎和C一样好(远远不是其他),但Scala和Clojure(两种在JVM上运行的函数语言)都具有比OCaml,Erlang更好的性能。

并且有很多“Java比X更快”,(例如,这里有关于SO的问题: Java运行时性能与本机C / C ++代码? )。

因此,对于某些情况,Java似乎很快。 有人可以解释原因吗?

为什么字节码可能比本机代码运行得更快,在某些情况下,给定动态代码(Scala,Clojure)和垃圾收集? 为什么如果它更快,仍有延迟?

这似乎是一个矛盾,有人可以摆脱光明吗?

詹姆斯·戈斯林在书中策划了编程,他解释说:

詹姆斯 :没错。 这些天我们总是在击败真正优秀的C和C ++编译器。 当你转到动态编译器时,当编译器在最后一刻运行时,你会获得两个优势。 一个是你确切知道你正在运行的芯片组。 很多时候,当人们编译一段C代码时,他们必须编译它以运行在通用的x86架构上。 你得到的二进制文件几乎都没有特别适合它们。 您下载了Mozilla的最新副本,它几乎可以在任何英特尔架构CPU上运行。 几乎有一个Linux二进制文件。 它非常通用,它是用GCC编译的,它不是一个非常好的C编译器。

当HotSpot运行时,它确切地知道您正在运行的芯片组。 它确切知道缓存的工作原理。 它确切知道内存层次结构的工作原理。 它确切地知道所有管道互锁在CPU中如何工作。 它知道这个芯片有哪些指令集扩展。 它可以精确地优化您所使用的机器。 然后另一半是它实际上看到应用程序正在运行。 它能够获得知道哪些事情很重要的统计数据。 它能够内联C编译器永远无法做到的事情。 在Java世界中内联的东西非常惊人。 然后,您将了解存储管理与现代垃圾收集器的工作方式。 使用现代垃圾收集器,存储分配非常快。

快速JVM使用即时(JIT)编译。 字节码在运行时即时转换为本机代码。 JIT提供了许多优化机会。 有关JIT的更多信息,请参阅此维基百科文章 。

JVM有很多种类!

最快的是根据收集的性能特征,动态地将字节代码编译为本机代码。 所有这些都需要额外的内存,所以他们以内存为代价购买速度。 此外,最高速度需要一段时间才能达到,因此短期计划的好处并不显示。

即便如此, JamVM解释器(与Oracle JVM相比很小)仍然可以达到编译JVM合理分数的最高速度。

关于垃圾收集,速度再次来自拥有足够的内存。 此外,真正的好处来自于从代码中删除责任以跟踪对象何时不再使用。 这导致更简单,更不容易出错的程序。

这是一个古老的论点。 几乎所有流行的都是Emacs和VI。(但绝对不是那么老)。 我已经看到很多c ++开发人员提供了很多关于为什么大多数性能基准测试(特别是提到java如何像c ++ __一样快)的论点是偏斜的,并且说实话他们有一个观点。

我没有足够的知识或时间深入了解Java如何像C ++一样快,但以下是它可能是……的原因……

1-当你要求两个非常有能力的开发人员用Java和C ++编写代码来解决现实世界的问题(同样的问题)时,如果java的执行速度比C ++快,我会感到惊讶。 编写良好的C ++使用Java将使用的一小部分内存。 (Java对象稍微膨胀)。

2- Java本身就是一种更简单的语言,它的编写是为了确保次优代码难以编写。 通过抽象内存管理以及处理低级优化,它很容易用Java编写好的代码而不是c ++。 (我认为这是最重要的事情……难以用Java编写糟糕的代码)。另一方面,优秀的C ++开发人员可以比java中的自动GC更好地处理内存管理。 (Java将所有内容存储在堆中,因此使用更多内存…)

3- Java编译器已经得到了持续改进,并且像热点这样的想法已被certificate比营销术语更好。 (当JIT推出时,它只是一个营销术语……根据我…… :))

4-人机工程学或基于底层操作系统参数定制设置使得Java句柄变化更好。 所以在某些环境中,Java的表现并不像C ++那么好。

5-为Java开发人员开放高级并发和并行api也是一个原因。 Java并发包可以说是编写可以利用当今多处理器环境的高性能代码的最简单方法。

6-随着硬件变得越来越便宜,开发人员的能力已经成为一个更大的因素,这就是为什么我相信大量的c ++代码可能比Java慢。

Java字节代码比大多数本机操作码更容易优化。 由于字节代码受到限制,并且您无法执行某些危险操作,因此可以更加充分地进行优化。 例如,使用指针别名。 http://en.wikipedia.org/wiki/Pointer_aliasing

在c / c ++中你可以做到

char[] somebuffer = getABuffer(); char* ptr = &someBuffer[2]; memcpy(ptr, somebuffer, length); 

这使得在某些情况下很难进行优化,因为你无法确定是什么意思。 在Java中,这种事情是不允许的。

通常,您可以在更高抽象级别上执行的代码突变比在目标代码上更强大。