前言
直接記憶體大多時候也被稱為堆外記憶體,自從 JDK 引入 NIO 後,直接記憶體的使用也越來越普遍。透過 native 方法可以分配堆外記憶體,透過 DirectByteBuffer 物件來操作。
直接記憶體不屬於 Java 堆,所以它不受堆大小限制,但是它受物理記憶體大小的限制。
配置可以透過 -XX:MaxDirectMemorySize 引數來設定最大可用直接記憶體,如果啟動時未設定則預設為最大堆記憶體大小,即與 -Xmx 相同。即假如最大堆記憶體為1G,則預設直接記憶體也為1G,那麼 JVM 最大需要的記憶體大小為2G多一些。當直接記憶體達到最大限制時就會觸發GC,如果回收失敗則會引起OutOfMemoryError。
分配記憶體耗時環境為JDK9,兩種記憶體分配的耗時如下,執行兩遍讓其預熱。可以看到直接記憶體的分配比較耗時,而堆記憶體分配操作耗時少好幾倍。
public static void directMemoryAllocate() { long tsStart = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { ByteBuffer buffer = ByteBuffer.allocateDirect(400); } System.out.println("direct memory allocate: " + (System.currentTimeMillis() - tsStart) + " ms"); tsStart = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { ByteBuffer buffer = ByteBuffer.allocate(400); } System.out.println("heap memory allocate: " + (System.currentTimeMillis() - tsStart) + " ms"); }
direct memory allocate: 149 msheap memory allocate: 41 msdirect memory allocate: 122 msheap memory allocate: 31 ms
讀寫操作耗時
環境為JDK9,兩種記憶體的讀寫操作耗時如下,同樣執行兩遍讓其預熱,可以看到直接記憶體讀寫操作的速度相對快一些。
public static void memoryRW() { ByteBuffer buffer = ByteBuffer.allocateDirect(400); ByteBuffer buffer2 = ByteBuffer.allocate(400); long tsStart = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { for (int j = 0; j < 100; j++) { buffer.putInt(j); } buffer.flip(); for (byte j = 0; j < 100; j++) { buffer.getInt(); } buffer.clear(); } System.out.println("direct memory rw: " + (System.currentTimeMillis() - tsStart) + " ms"); tsStart = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { for (int j = 0; j < 100; j++) { buffer2.putInt(j); } buffer2.flip(); for (byte j = 0; j < 100; j++) { buffer2.getInt(); } buffer2.clear(); } System.out.println("heap memory rw: " + (System.currentTimeMillis() - tsStart) + " ms"); }
direct memory rw: 39 msheap memory rw: 34 msdirect memory rw: 23 msheap memory rw: 46 ms
總結
理論上直接記憶體的機制訪問速度要快一些,但也不能武斷地直接說直接記憶體快,另外,在記憶體分配操作上直接記憶體要慢一些。直接記憶體更適合在記憶體申請次數較少,但讀寫操作較頻繁的場景。
最新評論