在什麼情況下,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提供一些這方面優化,稱為“全程式優化”或“連結時優化”(http://gcc.gnu.org/wiki/LinkTime…),允許在工程範圍內越過物件檔案進行內聯。但是,基本上還是不允許通過動態連結的方式來實現內聯(如通過內聯的方式實現zlib的呼叫等)。許多大型專案都是通過複製標準庫的功能到它們的程式碼中來實現。
原文連結: quora 翻譯: ImportNew.com
相關文章
- GreatSQL 中 Insert 慢是什麼情況?SQL
- Java類什麼情況下被初始化?Java
- 什麼是java序列化?什麼情況下需要序列化?Java
- 企業在什麼情況下引入分散式資料庫?分散式資料庫
- 爬蟲在什麼情況下才需要使用代理IP爬蟲
- 什麼情況下你能接受 996996
- 什麼情況下會出現css阻塞?CSS
- 什麼情況下會出現js阻塞?JS
- 什麼情況下進行效能測試
- 【知識分享】企業在什麼情況下需要做負載均衡負載
- 在什麼情況下可採取區塊鏈的流程圖 - TProphet區塊鏈流程圖
- ORACLE expdp在表空間較多的情況下執行非常緩慢Oracle
- 股票抄底絕招 什麼情況下抄底合適
- 什麼情況下需要搭建大資料平臺大資料
- vue什麼情況下需要用到this.$nextTickVue
- 香港伺服器什麼情況下需要更新升級?伺服器
- Linux中什麼情況下會發生程式排程?Linux
- Snowflake(雪花演算法),什麼情況下會衝突?演算法
- Nature回應:為什麼在沒有程式碼的情況下發布AlphaFold3?
- 拼多多開店需要了解哪些規則?在什麼情況下會被罰款?
- Go高階特性 12 | 指標詳解:在什麼情況下應該使用指標?Go指標
- 什麼情況!華為開源JDK!JDK
- java面試一日一題:講下在什麼情況下會發生類載入Java面試
- oracle組合索引什麼情況下生效?Oracle索引
- 容器化,微服務,DevOps,什麼情況下會三位一體?微服務dev
- 請在這幾種情況下匯入TPM管理
- 4.3.4.1 在不使用Oracle OMF的情況下建立CDBOracle
- 在不影響程式使用的情況下新增shellcode
- 成都現在的情況
- 什麼情況下需要進行靜態程式分析?常用Java靜態程式碼分析工具的優勢Java
- 一般情況下,大圖片需要壓縮到什麼程度呢?
- CPU超頻小知識:電腦CPU什麼情況下需要超頻?
- 驗證碼不顯示是什麼情況
- 什麼情況下不要用Rust語言? - kerkourRust
- 一個頁面繼承了base.html的內在是什麼情況?繼承HTML
- 在不安裝oracle客戶端的情況下,使用PLSQLOracle客戶端SQL
- 棧空間受限情況下C/C++函式呼叫注意事項C++函式
- 【Java】JDK1.8之前HashMap併發情況為什麼會發生死迴圈JavaJDKHashMap
- 應用架構設計中,什麼情況下開始考慮快取?應用架構快取