当前位置: 首页 > 科技观察

什么情况下Java会比C++慢很多?

时间:2023-03-14 15:25:20 科技观察

Q:Java在什么情况下明显比C++慢?回答:BenMaurer:为了回答这个问题,您需要将问题分为几个可能导致缓慢的原因:垃圾收集器。这是一把“双刃剑”。如果您的程序遵循“大多数对象死于新生代”模型,那么垃圾收集器将非常有益(更少的碎片,更好的缓存局部性)。但是,如果程序不遵循这个模型,JVM就会花费大量资源回收堆内存。大物体。在Java中,所有的对象都有一个vtable指针,在C++中使用POD结构没有额外的开销。此外,可以锁定所有Java对象。它的实现依赖于JVM,这可能需要向对象添加额外的字段。更大的对象==要缓存的对象更少==更慢。(另一方面,Java7使用64位记录来压缩指针,这也是问题的一部分。缺少内联对象。在Java中,所有类都是指针。在C++中,对象可以与其他对象一起分配,或者在栈上分配。这样可以提高缓存的局部性,从而减少动态内存分配的开销。客户端的C++代码频繁,它会增加很多开销。低效的强制抽象。例如,字符串在Java中是不可变的。如果你想写一个XML解析器,你只使用String对象(没有char[]),它会变慢是因为需要分配额外的空间,虚函数调用增加,在JVM中,几乎所有的函数调用都是虚函数调用,有很多代码尽量避免虚函数调用,但是很多场景,JVM解决不了这个问题.这可以防止内联的代码,使代码变慢。缺少高级编译功能和转换为汇编的能力。如果你写了一段可以从汇编中获益的代码,Java可能表现不佳。在我看来,最大的问题是垃圾回收。在程序中,强制在大内存中进行多次fullGC是造成Java和C++差距的最可能原因之一。此外,如果您将程序的工作集保留在L2缓存之外,诸如大对象、缺少内联对象等问题也会产生巨大的差异。低效的强制抽象和平台功能也会导致速度下降,但这通常只发生在底层代码上。如果您使用编写良好的Java代码库,这通常不是什么大问题。回答:ToddLipcon我基本上同意BenMaurer(嗨,Ben!)的回答。有几个细微差别:在现代JVM中,当分配永远不会从(a)本地函数或(b)本地线程中逃逸时,逃逸分析有效地确定固定分配。也就是说,当分配不需要加锁时,通常是在自己的栈空间上进行。这两种情况都是简单的“bumpthepointer”分配,相当于C中的stackallocation。译者注:EscapeAnalysis,逃逸分析,是一种编译优化技术,指的是分析指针动态范围的方法。通俗地说,当指向一个对象的指针被多个方法或线程引用时,我们称该指针发生转义。指针碰撞(碰撞点)。假设Java堆中的内存是绝对有规律的,所有已使用的内存都放在一边,空闲的内存放在另一边,中间放一个指针作为分界点的指针,那么分配的内存就是把那个指针移动到自由空间的距离等于对象的大小。这种分配方法称为“指针冲突”。即使不进行逃逸分析,新生代的分配也是通过指针碰撞在线程本地分配缓冲区(TLAB)中完成的,不需要同步。因此,Java中小对象的分配有时比C语言实现的malloc()方法更快。更好的malloc方法,如Google的tcmalloc,采用了类似的方法。但是,由于C语言不能在内存中重新分配分配的对象,所以某些方面会受到限制。尽管存在内联和虚函数的问题,但实际上,Java在某些情况下甚至可以做得比C更好。特别是,C无法通过动态链接实现内联,因为内联是在编译时执行的,而不是运行时。另一方面,Java可以跨不同的类或库边界动态地内联函数,即使该类的实际实现在编译时尚不可用。在许多任务中,这种方法比总是需要调用vtables的C++虚函数调用更有效。而JIT编译器,如果之前的动态属性已经丢失(比如加载了一个新类),可以智能取消内联优化。较新版本的GCC在这方面提供了一些优化,称为“全程序优化”或“链接时优化”(http://gcc.gnu.org/wiki/LinkTime…),它允许跨对象的项目范围内省档案对联。但是基本上不允许通过动态链接实现内联(比如通过内联实现zlib调用等)。许多大型项目通过将标准库功能复制到它们的代码中来实现这一点。原文链接:quora翻译:ImportNew.com-paddx翻译链接:http://www.importnew.com/16218.html