java nio bytebuffer wrap




ByteBuffer.allocate()與ByteBuffer.allocateDirect() (3)

因為DirectByteBuffers是操作系統級別的直接內存映射

他們不是。 它們只是普通的應用程序進程內存,但不受Java GC期間的重定位影響,這大大簡化了JNI層中的內容。 你所描述的適用於MappedByteBuffer

它會更快地執行get / put調用

結論並不遵循前提; 前提是錯誤的; 結論也是錯誤的。 一旦進入JNI層,它們會更快,並且如果您正在讀取和寫入同一個DirectByteBuffer它們會更快,因為數據永遠DirectByteBuffer跨越JNI邊界。

allocate()allocateDirect() ,這就是問題所在。

多年來,我一直堅持認為,因為DirectByteBuffer是在操作系統級別的直接內存映射,所以它的get / put調用比HeapByteBuffer更快。 我從來沒有真正感興趣的是直到現在才找到有關情況的確切細節。 我想知道兩種類型的ByteBuffer的哪一種更快,以及在什麼條件下。


Ron Hitches在他出色的書Java NIO中似乎提供了我認為可以很好地回答你的問題的答案:

操作系統在內存區域執行I / O操作。 就操作系統而言,這些內存區域是連續的字節序列。 因此,只有字節緩衝區才有資格參與I / O操作並不奇怪。 另請注意,操作系統將直接訪問進程的地址空間,在此情況下為JVM進程,以傳輸數據。 這意味著作為I / O對象目標的內存區域必須是連續的字節序列。 在JVM中,字節數組可能不會連續存儲在內存中,或者垃圾收集器可以隨時移動它。 數組是Java中的對象,數據存儲在該對象內的方式可能因JVM實現而異。

出於這個原因,引入了直接緩衝區的概念。 直接緩衝區用於與通道和本地I / O例程交互。 他們盡最大努力將字節元素存儲在通道可用於直接或原始訪問的內存區域中,方法是使用本機代碼告訴操作系統直接排空或填充內存區域。

直接字節緩衝區通常是I / O操作的最佳選擇。 按照設計,它們支持JVM可用的最高效的I / O機制。 非本地字節緩衝區可以傳遞給通道,但這樣做可能會導致性能損失。 非直接緩衝區通常不可能成為本地I / O操作的目標。 如果將非直接的ByteBuffer對像傳遞給要寫入的通道,則該通道可能會在每次調用時隱式執行以下操作:

  1. 創建一個臨時直接的ByteBuffer對象。
  2. 將非直接緩衝區的內容複製到臨時緩衝區。
  3. 使用臨時緩衝區執行低級I / O操作。
  4. 臨時緩衝區對象超出範圍並最終被垃圾收集。

這可能會導致緩衝區復制和每個I / O上的對象流失,這正是我們想要避免的事情。 但是,根據實施情況,情況可能不會那麼糟糕。 運行時可能會緩存和重用直接緩衝區或執行其他巧妙的技巧來提高吞吐量。 如果您只是創建一次性使用的緩衝區,則差異不顯著。 另一方面,如果您將在高性能場景中重複使用緩衝區,則最好分配直接緩衝區並重新使用它們。

直接緩衝區對於I / O是最佳的,但是它們的創建可能比非直接字節緩衝區更昂貴。 直接緩衝區使用的內存通過調用本地操作系統特定的代碼來分配,繞過標準的JVM堆。 根據主機操作系統和JVM實現的不同,設置和拆除直接緩衝區可能比堆駐留緩衝區貴得多。 直接緩衝區的內存區不受垃圾回收處理,因為它們不在標準JVM堆中。

使用直接緩衝區和非直接緩衝區的性能權衡取決於JVM,操作系統和代碼設計。 通過在堆外部分配內存,您可能會將應用程序置於JVM不知道的其他外力之下。 在發揮其他運動部件的作用時,確保達到預期的效果。 我推薦以前的軟件格言:首先讓它工作,然後讓它快速。 先不要過分擔心優化; 首先要關注正確性。 JVM實現可能能夠執行緩衝區緩存或其他優化,這將為您提供所需的性能,而無需您付出大量不必要的努力。


沒有理由期望直接緩衝區在jvm 訪問更快。 當你將它們傳遞給本地代碼時,它們的優勢就來了 - 例如各種渠道背後的代碼。





bytebuffer