在什么情况下,Java比C++慢很多?
问:在什么情况下,Java 比 C++ 慢很多?
答:Ben Maurer:
为了回答这个问题,需要先将该问题分成几个可能引起慢的原因:
垃圾回收器。这是一把“双刃剑”。如果你的程序遵循“大部分对象都在年青代中消亡”模型,垃圾回收器是非常有利的(很少的碎片,更好的缓存局部性)。但是,如果程序不遵循该模型,JVM将花费很多资源来回收堆内存。
大对象。在Java中,所有的对象都有一个vtable指针,而C++中使用POD结构没有额外开销。此外,所有的Java对象是可以被锁定的。其实现依赖于JVM,这可能需要在对象中增加额外的字段。大对象 == 缓存更少的对象 == 更慢。(另一方面,Java 7 用64位记录压缩后的指针,这也是造成该问题的一部分原因。
缺乏内联对象。在Java中,所有的类都是指针。在C++中,对象可以和其它对象一起分配,或者在栈上分配。这样可以提高缓存的局部性,从而减少动态内存分配的开销。
平台函数调用。在Java中,JNI的调用或者将对象编译成本地代码都会带来不小的开销。如果你需要频繁调用客户端的C++代码,会增加很大的开销。
低效的强制抽象。例如,在Java中字符串是不可变的。如果你想写一个XML分析器,你只使用String对象(没有char[]),它将会很慢,因为需要分配额外的空间。
虚函数调用增加。JVM中,几乎所有的函数调用都是虚函数调用。有许多代码尝试避免虚函数调用,但是很多场景下,JVM无法解决这个问题。这阻碍了代码的内联,使代码变慢。
缺乏高级的编译特征及转为汇编的能力。 如果你写了一段能从汇编得益的代码Java可能表现不佳。
在我看来,最大的问题是垃圾回收。在程序中,强制在大的内存中进行多次完全GC,是最容易导致Java和C++之间产生鸿沟的原因之一。除此之外,如果将程序的工作集放在L2缓存之外,像大对象、缺乏内联对象等问题,也会导致两者之间的巨大差别。
低效的强制抽象和平台函数也会导致速度下降,但是这通常只会因为低级的代码才会产生。如果你使用写得很好的Java代码库,这通常不是什么大问题。
答:Todd Lipcon
我基本同意Ben Maurer(hey Ben!)的回答。有几个小点不同:
在最新的JVM中,当这种分配永远不会从(a)局部函数或(b)局部线程逃逸出去的时候,逃逸分析能有效地决定一种固定分配。也就是说当分配不需要加锁,通常是在自身的栈空间上进行的。这两种情况下都是一种简单的“指针碰撞(bump the pointer)”分配,这等同于C中的栈分配。
译者注:
逃逸分析 Escape Analysis,是一种编译优化技术,指分析指针动态范围的方法。通俗地说,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。
指针碰撞(bump the point)。假设Java堆中内存是绝对规整的,所有用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”。
即使没有逃逸分析,年青代的分配也是通过指针碰撞方式,在线程本地分配缓冲区(TLAB)中完成的,不需要进行同步。所以Java中小对象的分配有的时候比C语言实现的 malloc() 方式更快。更好的 malloc 方法像Google的 tcmalloc,采用了类似的方式。但是由于C语言无法在内存中对分配后的对象重新分配,所以某些方面会受到限制。
虽然存在内联和虚函数问题,但是实际上,Java在某些情况下甚至可以做的比C更好。特别是,C不能通过动态链接功能来实现内联,因为内联是在编译时期进行的,而不是运行时期。而Java可越过不同的类或库的边界来动态内联一个函数,即使该类的真正实现在编译期间还不可用。许多工作中,这种方式比C++的虚函数调用更有效,C++虚函数调用总是需要调用虚表。而JIT编译器,如果之前动态属性已经丢失(如新的类已经被加载),能够聪明地取消内联优化。
新版本的GCC提供一些这方面优化,称为“全程序优化”或“链接时优化",允许在工程范围内越过对象文件进行内联。但是,基本上还是不允许通过动态链接的方式来实现内联(如通过内联的方式实现zlib的调用等)。许多大型项目都是通过复制标准库的功能到它们的代码中来实现。

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Java 8 memperkenalkan API Stream, menyediakan cara yang kuat dan ekspresif untuk memproses koleksi data. Walau bagaimanapun, soalan biasa apabila menggunakan aliran adalah: bagaimana untuk memecahkan atau kembali dari operasi foreach? Gelung tradisional membolehkan gangguan awal atau pulangan, tetapi kaedah Foreach Stream tidak menyokong secara langsung kaedah ini. Artikel ini akan menerangkan sebab -sebab dan meneroka kaedah alternatif untuk melaksanakan penamatan pramatang dalam sistem pemprosesan aliran. Bacaan Lanjut: Penambahbaikan API Java Stream Memahami aliran aliran Kaedah Foreach adalah operasi terminal yang melakukan satu operasi pada setiap elemen dalam aliran. Niat reka bentuknya adalah

Punca dan penyelesaian untuk kesilapan Apabila menggunakan PECL untuk memasang sambungan dalam persekitaran Docker Apabila menggunakan persekitaran Docker, kami sering menemui beberapa sakit kepala ...

Dalam C, jenis char digunakan dalam rentetan: 1. Simpan satu watak; 2. Gunakan array untuk mewakili rentetan dan berakhir dengan terminator null; 3. Beroperasi melalui fungsi operasi rentetan; 4. Baca atau output rentetan dari papan kekunci.

Kapsul adalah angka geometri tiga dimensi, terdiri daripada silinder dan hemisfera di kedua-dua hujungnya. Jumlah kapsul boleh dikira dengan menambahkan isipadu silinder dan jumlah hemisfera di kedua -dua hujungnya. Tutorial ini akan membincangkan cara mengira jumlah kapsul yang diberikan dalam Java menggunakan kaedah yang berbeza. Formula volum kapsul Formula untuk jumlah kapsul adalah seperti berikut: Kelantangan kapsul = isipadu isipadu silinder Dua jumlah hemisfera dalam, R: Radius hemisfera. H: Ketinggian silinder (tidak termasuk hemisfera). Contoh 1 masukkan Jejari = 5 unit Ketinggian = 10 unit Output Jilid = 1570.8 Unit padu menjelaskan Kirakan kelantangan menggunakan formula: Kelantangan = π × r2 × h (4

Spring Boot memudahkan penciptaan aplikasi Java yang mantap, berskala, dan siap pengeluaran, merevolusi pembangunan Java. Pendekatan "Konvensyen Lebih Konfigurasi", yang wujud pada ekosistem musim bunga, meminimumkan persediaan manual, Allo

Stack adalah struktur data yang mengikuti prinsip LIFO (terakhir, pertama keluar). Dalam erti kata lain, elemen terakhir yang kita tambahkan pada timbunan adalah yang pertama dikeluarkan. Apabila kita menambah (atau menolak) unsur ke timbunan, mereka diletakkan di atas; iaitu di atas semua

Multithreading dalam bahasa dapat meningkatkan kecekapan program. Terdapat empat cara utama untuk melaksanakan multithreading dalam bahasa C: Buat proses bebas: Buat pelbagai proses berjalan secara bebas, setiap proses mempunyai ruang ingatan sendiri. Pseudo-Multithreading: Buat pelbagai aliran pelaksanaan dalam proses yang berkongsi ruang memori yang sama dan laksanakan secara bergantian. Perpustakaan multi-threaded: Gunakan perpustakaan berbilang threaded seperti PTHREADS untuk membuat dan mengurus benang, menyediakan fungsi operasi benang yang kaya. Coroutine: Pelaksanaan pelbagai threaded ringan yang membahagikan tugas menjadi subtask kecil dan melaksanakannya pada gilirannya.

Idea Intellij memudahkan pembangunan boot musim bunga, menjadikannya kegemaran di kalangan pemaju Java. Pendekatan konfigurasi konvensyen-over-configurationnya meminimumkan kod boilerplate, yang membolehkan pemaju memberi tumpuan kepada logik perniagaan. Tutorial ini menunjukkan dua meto
