29

我正在阅读有关此答案的评论,并看到了这句话。

对象实例化和面向对象的特性使用起来非常快(在许多情况下比 C++ 更快),因为它们是从一开始就设计好的。收集速度很快。标准 Java 在这方面优于标准 C/C++,即使对于最优化的 C 代码也是如此。

一位用户(我可能会添加非常高的代表)大胆地为这一说法辩护,称

  1. java中的堆分配比C++的好

  2. 并添加了这个声明来保护java中的集合

    与 C++ 集合相比,Java 集合的速度更快,这主要是由于不同的内存子系统。

所以我的问题是这是否真的是真的,如果是这样,为什么 java 的堆分配这么快。

4

5 回答 5

45

这种说法很荒谬;制作它的人要么非常不知情,要么非常不诚实。尤其是:

  • 这两种情况下动态内存分配的速度将取决于动态内存使用的模式以及实现。对于熟悉这两种情况下使用的算法的人来说,编写一个基准来证明他想要更快的算法是微不足道的。(因此,例如,使用大型、复杂图形的程序在构建、然后拆除和重建时,通常会在垃圾收集下运行得更快。就像从不使用足够动态内存来触发收集器的程序一样。使用少量、大型、通过手动内存管理,长寿命分配通常会运行得更快。)

  • 在比较集合时,您必须考虑集合中的内容。例如,如果您要比较 的大向量 double,Java 和 C++ 之间的差异可能很小,并且可能是任何一种方式。如果您要比较 的大向量Point,其中Point是一个包含两个双精度值的值类,C++ 可能会将 Java 从水中吹走,因为它使用纯值语义(没有额外的动态分配),而 Java 需要动态分配每个 Point(并且没有动态分配总是比最快的动态分配更快)。如果PointJava 中的类被正确设计为一个值(因此是不可变的,例如java.lang.String),那么对 Point向量中的 进行转换将需要为每个 Point; 在 C++ 中,你可以分配。

  • 很大程度上取决于优化器。在 Java 中,优化器在完全了解实际用例的情况下工作,在这个特定的程序运行中,以及在这个运行中运行它的实际处理器的完美知识。在 C++ 中,优化器必须处理来自分析运行的数据,这些数据永远不会与程序的任何一次运行完全对应,并且优化器必须(通常)生成将在各种处理器上运行(并快速运行)的代码版本。另一方面,C++ 优化器可能会花费更多时间来分析不同的路径(有效的优化可能需要大量 CPU);Java 优化器必须相当快。

  • 最后,尽管并非与所有应用程序都相关,但 C++ 可以是单线程的。在这种情况下,分配器中不需要锁定,而在 Java 中从来没有这种情况。

关于这两个编号点:C++ 在其堆分配器中可以使用或多或少与 Java 相同的算法。我使用过::operator delete()函数为空的 C++ 程序,并且内存被垃圾收集。(如果您的应用程序分配了许多短暂的、小对象,那么这样的分配器可能会加快处理速度。)至于第二个:C++ 真正的一大优势是它的内存模型不需要动态分配所有内容。即使 Java 中的分配只需要 C++ 中的十分之一(如果您只计算分配,而不是收集器扫描所需的时间,可能就是这种情况),具有大向量Point,如上所述,您将 C++ 中的两个或三个分配与 Java 中的数百万个分配进行比较。

最后:“为什么 Java 的堆分配这么快?” 如果您摊销收集阶段的时间,则不一定。分配本身的时间可能非常便宜,因为 Java(或至少大多数 Java 实现)使用重定位收集器,这导致所有空闲内存都位于单个连续块中。这至少部分抵消了收集器所需的时间:为了获得这种连续性,您必须移动数据,这意味着大量的复制。在大多数实现中,它还意味着指针中的额外间接,以及许多特殊逻辑来避免当一个线程在寄存器中具有地址时出现问题等。

于 2013-08-16T09:07:33.650 回答
30

你的问题没有具体的答案。例如,C++ 根本没有定义内存管理。它将分配细节留给库实现。因此,在 C++ 的范围内,给定的平台可能有一个非常慢的堆分配方案,如果绕过它,Java 肯定会更快。在另一个平台上,内存分配可能非常快,性能优于 Java。正如 James Kanze 所指出的,Java对内存管理的限制也很少(例如,即使是 GC 算法也完全取决于 JVM 实现者)。因为 Java 和 C++ 对内存管理没有限制,所以这个问题没有具体的答案。C++ 有意开放底层硬件和内核功能,Java 有意开放 JVM 内存管理。所以这个问题变得非常模糊。

您可能会发现在 Java 中有些操作更快,而有些则不然。但是,除非您尝试,否则您永远不会知道

在实践中,真正的区别在于您的更高级别的算法和实现。对于除了最绝对性能关键的应用程序之外的所有应用程序,与算法本身的性能特征相比,不同语言中相同数据结构的性能差异完全可以忽略不计。专注于优化更高级别的实现。只有在你这样做之后,在你确定你的性能要求没有得到满足,并且在你进行基准测试并发现(不太可能)你的瓶颈在容器实现之后,你才应该开始考虑这样的事情。

一般来说,一旦你发现自己在思考或阅读 C++ 与 Java 的问题,就停下来重新专注于一些富有成效的事情。

于 2013-08-16T07:43:18.647 回答
16

Java 堆更快,因为(简化)分配所需要做的就是增加堆顶指针(就像在堆栈上一样)。这是可能的,因为堆是定期压缩的。所以你的速度代价是:

  1. 定期 GC 暂停以进行堆压缩
  2. 内存使用量增加

没有免费的奶酪......因此,虽然收集操作可能很快,但在 GC 工作期间整体减慢了它的摊销。

于 2013-08-16T07:40:29.840 回答
12

虽然我是 Java 的粉丝,但值得注意的是 C++ 支持在堆栈上分配对象,这比堆分配要快。

如果您通过各种方式有效地使用 C++ 来做同样的事情,它会比 Java 更快(即使您需要更长的时间才能找到最佳组合)

如果您像在 Java 中一样使用 C++ 编程,例如堆上的所有内容、所有虚拟方法、有很多运行时检查,这些检查不做任何事情并且可以动态优化,那么它会更慢。Java 进一步优化了这些东西,因为这些 a) 是 Java 唯一做的事情,b) 可以更有效地动态优化,c) Java 的功能和副作用更少,因此优化器更容易获得不错的速度。

于 2013-08-16T07:45:16.540 回答
1

收集速度很快。标准 Java 在这方面优于标准 C/C++,即使对于最优化的 C 代码也是如此。

这对于特定的集合可能是正确的,但对于所有使用模式中的所有集合肯定不是正确的。

例如, ajava.util.HashMap将优于 a std:map,因为后者需要进行排序。也就是说,Java 标准库中最快的 Map 比 C++ 中最快的 Map 更快(至少在 C++11 之前,它添加了std:unordered_map.

另一方面, a比 astd:Vector<int>效率高得多java.util.ArrayList<Integer>(由于类型擦除,您不能使用 a java.util.ArrayList<int>,因此最终会消耗大约 4 倍的内存,并且缓存局部性可能较差,因此迭代速度也相应较慢)。

简而言之,就像大多数笼统的概括一样,这并不总是适用。但是,相反的断言也不会(Java 总是比 C++ 慢)。这实际上取决于细节,例如您如何使用该集合,甚至是您比较的语言版本)。

于 2013-08-19T17:47:19.923 回答