Java 真的很慢吗?

Java 有 在某种程度上因为反应迟钝而名声在外

  • Java 真的很慢吗?
  • 如果是,为什么?瓶颈在哪里?是因为 JVM 效率低下吗?垃圾收集?纯字节码库代替 JNI 封装的 C 代码?许多其他语言都具有这些特性,但它们并没有这种因速度慢而闻名的特性。
90084 次浏览

Java 是一种高级语言,如今它的声誉是具有与其他可比较的高级语言相当的性能。

  1. 它有 动态绑定动态绑定语义。与将非虚方法编译为函数调用的 C + + 相比,即使是世界上最好的 Java 编译器也不得不生成效率较低的代码。但它也是一种更清晰、更高级的语义。

  2. 我不记得细节了,但是我听说在 Java 的早期,每个 Java 对象都有一个互斥锁,每个方法都需要获取并释放它。这往往会使它更好地适应并发,尽管不幸的是,每个对象只有一个互斥对象不能保护您免受竞争、死锁或任何可能发生在并发程序中的坏事情的影响。这部分,如果是真的,是有点天真,但它来自良好的意图。如果你对这方面有更多的了解,请随时告诉我详情。

  3. Java 作为高级语言的另一种方式是使用 垃圾收集。对于一次性分配所有需要和使用的内存的程序,垃圾收集可能比 mallocfree慢。问题是,在没有垃圾收集的语言中,程序员倾向于编写 只有程序,一次性分配所有他们需要的内存,如果发现某个任意的最大大小常数已经超出,程序就会失败。所以这个对比就是苹果和橙子。当程序员用非 GC 语言编写和调试具有链式结构动态分配的程序时,他们有时会发现他们的程序不再比 GC 语言快,因为 mallocfree不是免费的!它们也有开销... ... 另外,没有 GC 强制指定谁释放什么,而且不得不指定谁释放什么,有时候会强制你复制数据ーー当几个函数需要数据而且不清楚哪个函数最后会使用数据时ーー而在 GC 语言中不需要复制。

它是 Java 早期(20世纪90年代中后期)的过时信息。与之前的版本相比,Java 的每个主要版本都引入了显著的提速。随着 Oracle 明显地将 JRockit 与 Sun 的 JVM 合并到 Java7中,这种趋势看起来将继续下去。

与许多其他流行的现代语言(Python、 Ruby、 PHP)相比,Java 在大多数情况下的运行速度要快得多。它并不完全匹配 C 或 C + + ,但对于许多任务来说,它已经足够接近了。真正的性能问题应该是它最终使用了多少内存。

最初 Java 并不是特别快,但也不是特别慢。现在,Java 非常快。和我交谈过的人对 Java 速度慢的印象来自两个方面:

  1. 缓慢的 VM 启动时间。与本机应用程序相比,早期的 Java 实现需要很长时间才能启动和加载所需的库和应用程序。

  2. 慢用户界面。早期的 摇摆是缓慢的。大多数 Windows 用户发现默认的 Metal L & F 很丑,这可能也于事无补。

鉴于以上几点,难怪人们会有“ Java 很慢”的印象。

对于习惯于开发本地应用程序,甚至是 VisualBasic应用程序的用户或开发人员来说,这两点是应用程序中最明显的,也是你对应用程序的第一印象(除非它是非 GUI 应用程序,在这种情况下只有1。适用).

当应用程序启动需要8秒钟,而他的旧的 VisualBasic 应用程序立即启动时,您不会让用户相信“它执行代码非常快”——即使代码执行和启动时间可能根本没有关联。

破坏第一印象是一个伟大的方式开始谣言和神话。谣言和神话是很难消灭的。

简而言之,Java 并不慢。“ Java 是慢的态度”的人是基于10多年前对 Java 的第一印象。

在上世纪90年代中期,当 Java 成为主流时,C + + 是主流语言,而 web 仍然相当新。此外,JVM 和 GC 是主流开发中相对较新的概念。早期的 JVM 有点慢(相对于在裸机上运行的 C + +) ,而且有时垃圾收集暂停时间很长,这导致 Java 慢的名声。

正如 Pascal 所说,Java 与其他高级语言不相上下。然而,作为在 Windows 98上使用原始 JVM 的人,当时 Java 虚拟机提供的抽象级别,可以说是痛苦的。

基本上,我们今天在 JVM 中认为理所当然的是带有很少或根本没有优化的软件仿真。

以前爪哇语很慢。由于 几代的性能增强,它变得更快了。上次我听说,它通常在 C # 速度的10% 以内——有时更快,有时更慢。

Javaapplet 启动仍然很慢,因为您必须启动一个完整的 JVM,它必须加载所有的类。有点像启动另一台电脑。一旦 JVM 启动,它会非常快,但是启动通常是人们所记住的。

而且,还有 至少有几个人永远不会相信 Java 的可行性。

现代 Java 是速度最快的语言之一,尽管它仍然占用大量内存。Java因为过去需要很长时间才能启动而被认为是慢的。

如果您仍然认为 Java 是缓慢的 ,请参阅 基准游戏结果。用提前编写的编译语言(c、 Fortran 等)编写的经过严格优化的代码可以打败它,但是,Java 的速度可以比 PHP、 Ruby、 Python 等快10倍以上。在某些特定领域,它可以击败常用的编译语言(如果它们使用标准库的话)。

现在没有“慢”Java 应用程序的借口了。开发人员和遗留代码/库是罪魁祸首,远远超过语言本身。还有,什么事都怪企业。'

公平地说,“ Java 很慢”的人群,这里有一些地方仍然很慢(2013年更新) :

  • 库通常是为了“正确性”和可读性而编写的,而不是为了性能。在我看来,这是 Java 仍然声名狼藉的主要原因,尤其是在服务器端。这使 String 问题成指数级恶化。一些简单的错误是常见的: 对象经常被用来代替原语,降低了性能并增加了内存使用。许多 Java 库(包括标准库)将频繁地创建 String,而不是重用可变或更简单的格式(char []或 StringBuffer)。这是缓慢的,并创建吨垃圾收集以后。为了解决这个问题,我建议开发人员尽可能使用原始集合,尤其是 Javalution 的库。

  • 字符串运算有点慢。Java 使用不可变的、 UTF-16编码的字符串对象。这意味着您需要更多的内存,更多的内存访问,而且有些操作比使用 ASCII (C,C + +)更复杂。当时,这是 对可移植性的决定,但是它带来了很小的性能成本。UTF-8现在看起来是个更好的选择。

  • 由于边界检查,数组访问比 C, 要慢一些。罚金过去很大,但现在很小(Java7优化了很多冗余的边界检查)。

  • 缺乏任意的内存访问会使某些 I/O 和位级处理变慢(例如压缩/解压缩)

  • Java 比 C 使用更多的内存, ,如果你的应用程序是内存绑定或内存带宽绑定(缓存等) ,这使得它更慢。另一方面,分配/释放速度极快(高度优化)。这是现在大多数高级语言的一个特性,这是由于对象和使用 < a href = “ http://en.wikipedia.org/wiki/Garbage _ Collection _% 28computer _ science% 29”rel = “ noReferrer”> GC 而不是显式的内存分配。加上错误的库决策。

  • 基于流的 I/O 由于(IMO,糟糕的选择)要求在每个流访问上进行同步,所以速度很慢。解决了这个问题,但是使用起来很痛苦。可以通过对数组进行读/写操作来解决这个问题,而不是一次只读一个元素。

  • Java 没有提供与 c 相同的低级功能,所以你不能使用肮脏的内联汇编技巧来使一些操作更快。这提供了可移植性,是现在大多数高级语言的一个特性。

  • 我们经常看到 Java 应用程序与非常老的 JVM 版本绑定在一起。特别是服务器端。与最新版本相比,这些旧版本的 JVM 可能效率低得令人难以置信。

最后,Java 被设计成以牺牲性能为代价来提供安全性和可移植性,并且对于一些真正需要的操作,它显示了。它的大部分“缓慢”名声已不再名副其实。


然而,有几个地方的 Java 是 快点比大多数其他语言:

  • 内存分配和释放 我见过这样的案例 在哪里它是20% 更快(或更多!) 分配一个新的多 kB 数组 重新使用缓存的文件

  • 对象实例化和面向对象特性使用 (在某些情况下比 C + + 快)非常迅速,因为它们是从一开始就设计好的。这部分来自于良好的 GC,而不是显式分配(显式分配对大量小对象分配更友好)。人们可以编写 C 来克服这个问题(通过滚动自定义内存管理和有效地执行 malloc) ,但这并不容易。

  • 方法调用基本上是免费的,在某些情况下比大方法代码更快。HotSpot编译器使用执行信息来优化方法调用,并且具有非常高效的内联。通过使用额外的执行信息,它有时可以胜过提前编译器,甚至(在极少数情况下)手动内联。与 C/C + + 相比,如果编译器决定不内联,那么方法调用会带来很小的性能损失。

  • 同步和多线程简单而有效。Java 从一开始就被设计成线程感知的,它显示了。现代计算机通常具有多核的特点,而且由于线程是内置于语言中的,因此可以很容易地利用这一优势。基本上,与标准的单线程 C 代码相比,可以额外提高100% 到300% 的速度。是的,精心编写的 C 线程和库可以克服这个问题,但是对于程序员来说,这是很多额外的工作。

  • 字符串包含长度: 有些操作更快。 使用空分隔字符串(C 中常见)。在 Java7中,Oracle 采用了 String.subString ()优化,因为人们在愚蠢地使用它,导致内存泄漏。

  • 数组副本高度优化。在最新的版本中,Java 为 System.arraycopy 使用手工调优的汇编程序。其结果是,在数组拷贝/memcopy 大量操作中,我看到我的代码比 C 语言中的等效操作多出了合理的空白。

  • JIT 编译器很善于使用 L1/L2缓存 。提前编译的程序不能根据所运行的特定 CPU 和系统实时调整代码。JIT 以这种方式提供了一些非常有效的循环转换。

其他一些历史事实也为“ Java 很慢”的名声做出了贡献:

  • 在 JIT 编译(Java 1.2/1.3)之前,语言只是解释,而不是编译,因此非常慢。
  • JIT 编译需要时间才能变得高效(每个版本都有重大改进)
  • 随着时间的推移,类加载变得越来越有效率,以前的类加载效率很低,启动过程也很慢。
  • Swing 和 UI 代码没有很好地使用本地图形硬件。
  • Swing 太糟糕了。我责怪 AWT 和 Swing 为什么 Java 从来没有在桌面上流行起来。
  • 在库类中大量使用同步; 现在提供了非同步版本
  • 小程序需要永远加载,因为通过网络传输一个完整的 JAR并加载 VM 来引导。
  • 同步过去常常带来严重的性能损失(这在每个 Java 版本中都进行了优化)。不过,反射仍然代价高昂。

人们通常会炫耀“这是解读”这句台词。因为曾经有一段时间,它是,负面新闻被那些抛弃 Java 的人们传递下来,他们认为 Java“太慢了”,再也没有回来测试更新的版本。

或者“人都是白痴”是个更好的答案。

对于大多数人的经验与它交互-Java慢。我们都看到过,在某个小应用程序出现之前,我们的浏览器上的咖啡杯就已经开始旋转了。启动 JVM 并下载 applet 二进制文件需要一段时间,这会以一种被注意到的方式影响用户体验。

缓慢的 JVM 启动和 applet 下载时间被明显地打上了 Java 咖啡杯的烙印,这也无济于事,因此人们将等待与 Java 联系在一起。当 闪念需要很长的时间来加载,“加载”消息的品牌是由 Flash 开发者指定的,所以人们不会责怪整个 Flash 技术。

所有这些都与 Java 在服务器上的性能无关,也与 Java 在浏览器之外的许多其他使用方式无关。但这是人们所看到的,也是非 Java 开发人员在思考 Java 时所记住的。

斯特凡诺:

我从一开始就使用 Java,所以从我的观点来看,缓慢的名声是由于没有响应和缓慢的 GUI 前端(AWT,然后是 Swing)和 Applet 中创建的,可能是因为虚拟机额外的缓慢启动时间。

Java 已经在 VM 地区规定并推动了大量的研究,并且已经有了相当多的改进,包括垃圾收集(实际上你可以调整很多东西,但是,我经常看到只使用默认值的系统)和热点优化(在开始的时候,可能在服务器端仍然更有效率)。

Java 在后端和计算级别上并没有那么慢,柯尔特就是最好的例子之一:

最新稳定的 Colt 版本打破了 JDK ibm-1.4.1、 RedHat 9.0、2x IntelXeon@2.8 GHz 上1.9 Gflop/s 的障碍。

主流 Java 之外还有很多东西需要考虑,比如 Realtime Java 或者提高速度的特殊机制,比如 飞跃,以及提前编译(比如 gcj)。此外,还有一些 IC 可以直接执行 Java 字节码,例如当前 iPhone 和 iPods ARM Jazelle中的 IC。

我认为今天这是一个政治决定(就像 iPhone/iPod 不支持 Java 一样) ,也是一个反对 Java 作为一种语言的决定(因为很多人认为它太冗长了)。

然而,现在还有很多其他的语言(例如 Python、 Ruby、 JavaScript、 Groovy、 Scala 等)可以作为 JavaVM 的替代品。

就我个人而言,我仍然喜欢它作为一个灵活可靠的平台,拥有出色的工具和库可用性,允许人们使用从最小的设备(例如 JavaCard)到最大的服务器的所有东西。

锤子在展开面团时比其他许多工具要慢得多。不会使锤子“慢下来”,也不会对它设计用来完成的任务不那么有用。

作为一种通用的编程语言,Java 与许多(如果不是大多数)编程任务相当。还有一些特定的、琐碎的测试,Java 在这些测试中的表现不会超过使用不那么复杂的语言(那些“更接近金属”的语言)手工编码的解决方案。

但是当涉及到“真实世界的应用程序”时,Java 通常是正确的工具。现在,也就是说,没有什么能够阻止开发人员使用任何工具制作性能较低的解决方案。滥用工具是一个众所周知的问题(看看 PHP 和 VB 的声誉就知道了)。然而,Java (大多数情况下)干净的设计和语法确实有助于减少误用。

这就是主要原因。还有很多其他的原因也没有起到作用: 最初,Java 的启动时间很慢(现在是固定的) ; Java 中的网络应用需要很长的时间才能下载(现在宽带普及,电影等大事情正在等待) ; UI Swing 没有(现在仍然没有)考虑到性能,所以它远不如 C + + 中的同类应用快捷。

如果我有以下资料:

<div class="apple-monkey"></div>
<div class="apple-horse"></div>
<div class="cow-apple-brick"></div>

我可以使用以下选择器查找前两个 DIV:

$("div[class^='apple-']")

“长启动时间”的罪魁祸首是动态链接。Java 应用程序由编译过的类组成。每个类通过 姓名引用其他类(对于参数类型、方法调用...)。JVM 必须在启动时检查并匹配这些名称。它以递增的方式执行,只在任何给定的时间执行它所需要的部分,但这仍然需要做一些工作。

但是,如果我有这个:

<div class="some-other-class apple-monkey"></div>
<div class="apple-horse"></div>
<div class="cow-apple-brick"></div>

在 C 应用程序中,链接阶段发生在编译结束时。它是缓慢的,特别是对于大型应用程序,但只有开发人员看到它。链接产生一个可执行文件,操作系统只需要“按原样”加载到 RAM 中。

它只会找到第二个 DIV,因为第一个 DIV 的类以字符串形式返回(我认为) ,并且实际上不是以‘ apple-’开头,而是以‘ some-’开头

在 Java 中,每次应用程序运行时都会发生链接,因此启动时间较长。

解决这个问题的一种方法是不使用开头,而是包含:

$("div[class*='apple-']")

各种各样的优化已经被应用,包括缓存技术,计算机变得更快(它们变得“更快”比应用程序变得“更大”) ,所以问题的重要性最近已经大大降低; 但是旧的偏见仍然存在。

问题是,在我的示例中,它还将选择第3个 DIV。

问: 通过 jQuery,对单个类名使用谓词选择器而不是将整个 class 属性作为字符串使用的正确方法是什么?是不是只需要获取 CLASS,然后将它分割成一个数组,然后使用 regex 遍历每个独立的数组?还是有更优雅/更简洁的解决方案?

至于之后的性能,我自己对数组访问(主要是哈希函数和其他加密算法)紧凑计算的基准测试通常表明,优化后的 C 代码比 Java 代码快3倍左右; 有时 C 只比 Java 快30% ,有时 C 可以快4倍,这取决于实现的算法。我看到一个10倍的因子,当“ C”代码实际上是大整数算术的汇编,由于64x64-> 128乘法操作码的处理器提供,但 Java 不能使用,因为其最长的整数类型是64位 long。这案子很棘手。在实际情况下,I/O 和内存带宽的考虑使得 C 代码的 真的速度比 Java 快三倍。

更简单[更快]。

把这些加在一起就会产生一个相当简单和自然的反应: Java 是缓慢的、丑陋的和笨拙的。由于大肆宣传它真的很快,有一种反应过度的倾向,并得出结论认为它是 糟透了慢,而不是(更准确地说)“稍微慢一点,主要是在特定的情况下。”对于用该语言编写前几个程序的开发人员来说,这通常是最糟糕的情况。在大多数语言中,“ hello world”程序的执行都是即时的,但是在 Java 中,当 JVM 启动时,会有一个很容易感觉到的停顿。即使纯解释器在紧循环上运行得慢得多,但对于这样的代码来说,仍然会显得更快,这仅仅是因为它可以加载并更快地开始执行。

以“ apple-”开头的类加上包含“ apple-”的类

$("div[class^='apple-'],div[class*=' apple-']")
T 类加载器可以执行很多 IO 操作。

许多 Java 桌面应用程序(比如 Eclipse)的 GUI 响应性很差,这可能是由于高内存消耗和类加载器可以执行大量 IO 操作这一事实。 情况有所好转,但几年前更糟。

情况有所好转,但几年前更糟。

许多(大多数)人喜欢做泛化,所以他们说“ Java 很慢”,因为他们认为当与应用程序交互时,应用程序很慢。

许多(大多数)人喜欢做泛化,所以他们说“ Java 很慢”,因为他们认为当与应用程序交互时,应用程序很慢。

在阅读了一页充满评论说 Java 并不慢的文章后,我不得不用不同的观点来回答。

通过添加更多的硬件来扩展您的应用程序,Java 对您来说可能是快速的。如果你认为在5-10范围内的常数因子加速不值得,你可能会考虑 Java 快。

语言的速度很大程度上取决于你对“快速”的期望。如果你认为 C # 很快,那么 Java 肯定也很快。如果您的问题域与数据库或半实时处理相关,那么 Java 肯定也足够快。如果您愿意通过添加更多的硬件来扩展应用程序,那么 Java 对您来说可能更快。如果你认为在5-10范围内的常数因子加速不值得,你可能会考虑 Java 快。

如果您在大型数据集上进行数值计算,或者绑定到一个 CPU 资源有限的执行环境,那么在5-10这个范围内的不断加速将是巨大的。即使是0.5的加速也可能意味着完成计算需要减少500小时。在这些情况下,Java 只是不允许您获得最后一码的性能,您可能会认为 Java 很慢。

如果您在大型数据集上进行数值计算,或者绑定到一个 CPU 资源有限的执行环境,那么在5-10这个范围内的不断加速将是巨大的。即使是0.5的加速也可能意味着完成计算需要减少500小时。在这些情况下,Java 只是不允许您获得最后一码的性能,您可能会认为 Java 很慢。

在这种情况下,作为问题乔希斯托多拉回答是正确的 以“ apple-”开头的类加上包含“ apple-”的类

$("div[class^='apple-'],div[class*=' apple-']")

Java 应用程序的主要问题是它是 巨大,因为库运行时库的规模很大。庞大的程序会占用大量内存并且倾向于交换,这意味着它们会变慢。

但是如果元素有多个这样的类

<div class="some-class apple-monkey"></div>
<div class="some-class apple-horse"></div>
<div class="some-class cow-apple-brick"></div>

那么 Josh Stodola 的解决方案就行不通了

Sun JVM 之所以很大,是因为它有一个非常好的字节码解释器,可以跟踪很多事情。这意味着有很多数据,也就是内存。

因为这个必须做这样的事情

$('.some-parent-class div').filter(function () {
return this.className.match(/\bapple-/);// this is for start with
//return this.className.match(/apple-/g);// this is for contain selector
}).css("color","red");

您可能想看看 jamvm 虚拟机,它是一个相当快速的解释器(没有本机代码) ,而且非常小。甚至开始得很快。

我的机器是一个惠普 核心2组合与3 GB 的内存。我使用64位 Ubuntu 10.04(Lucid Lynx)。

你可以把它放在任何地方:

$(function(){
$.expr[":"].acp = function(elem, index, m){
var regString = '\\b' + m[3];
var reg = new RegExp(regString, "g");
return elem.className.match(reg);
}
});

虽然这里的最佳答案是针对提问者的特殊情况的变通方案,但如果你正在寻找一种解决方案,在单个类名上实际使用“ start with”:

我这样做是因为我做了很多 GreaseMonkey 黑客的网站上,我没有后端控制,所以我经常需要找到具有动态后缀类名称的元素。非常有用。