JVM 堆 VS 本地內存

Java 的類實例一般在 JVM 堆上分配,而 Java 是通過 JNI 調用 C 代碼來實現 Socket 通信的,那麼 C 代碼在運行過程中需要的內存又是從哪裏分配的呢?C 代碼能否直接操作 Java 堆?

爲了回答這些問題,我先來說說 JVM 和用戶進程的關係。如果你想運行一個 Java 類文件,可以用下面的 Java 命令來執行

java my.class

這個命令行中的java其實是一個可執行程序,這個程序會創建 JVM 來加載和運行你的 Java 類。

操作系統會創建一個進程來執行這個java可執行程序,而每個進程都有自己的虛擬地址空間,JVM 用到的內存(包括堆、棧和方法區)就是從進程的虛擬地址空間上分配的。請你注意的是,JVM 內存只是進程空間的一部分,除此之外進程空間內還有代碼段、數據段、內存映射區、內核空間等。JVM 的角度看,JVM 內存之外的部分叫作本地內存,C 程序代碼在運行過程中用到的內存就是本地內存中分配的。下面我們通過一張圖來理解一下。
在這裏插入圖片描述

那 HeapByteBuffer 和 DirectByteBuffer 有什麼區別呢?HeapByteBuffer 對象本身在 JVM 堆上分配,並且它持有的字節數組byte[]也是在 JVM 堆上分配。

但是如果用HeapByteBuffer來接收網絡數據,需要把數據從內核先拷貝到一個臨時的本地內存,再從臨時本地內存拷貝到 JVM 堆,而不是直接從內核拷貝到 JVM 堆上。這是爲什麼呢?這是因爲數據從內核拷貝到 JVM 堆的過程中,JVM 可能會發生 GC,GC 過程中對象可能會被移動,也就是說 JVM 堆上的字節數組可能會被移動,這樣的話 Buffer 地址就失效了。如果這中間經過本地內存中轉,從本地內存到 JVM 堆的拷貝過程中 JVM 可以保證不做 GC。

如果使用 HeapByteBuffer,你會發現 JVM 堆和內核之間多了一層中轉,而 DirectByteBuffer 用來解決這個問題,DirectByteBuffer 對象本身在 JVM 堆上,但是它持有的字節數組不是從 JVM 堆上分配的,而是從本地內存分配的。

DirectByteBuffer 對象中有個 long 類型字段 address,記錄着本地內存的地址,這樣在接收數據的時候,直接把這個本地內存地址傳遞給 C 程序,C 程序會將網絡數據從內核拷貝到這個本地內存,JVM 可以直接讀取這個本地內存,這種方式比 HeapByteBuffer 少了一次拷貝,因此一般來說它的速度會比 HeapByteBuffer 快好幾倍。你可以通過上面的圖加深理解。

那爲什麼DirectByteBuffer的性能比HeapByteBuffer差依然在使用呢?
這是因爲本地內存不好管理,發生內存泄漏難以定位,從穩定性考慮,
HeapByteBuffer更好。

作者:taj3991
鏈接:https://www.jianshu.com/p/60afb21c8876
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章