1. 程式人生 > 實用技巧 >10. 直接記憶體

10. 直接記憶體

我們知道 在jdk8 之後,Hotspot 將 方法區的實現改為元空間,直接使用本地記憶體,下面就來詳細瞭解一下

1. 直接記憶體的概述

  1. 不是虛擬機器執行時資料區的一部分,也不是《Java虛擬機器規範》中定義的記憶體區域。
  2. 直接記憶體是在Java堆外的、直接向系統申請的記憶體區間。
  3. 起源於NIO,通過存在堆中的DirectByteBuffer操作Native記憶體
  4. 通常,訪問直接記憶體的速度會優於Java堆。即讀寫效能高。因此出於效能考慮,讀寫頻繁的場合可能會考慮使用直接記憶體。
  5. Java的NIO庫允許Java程式使用直接記憶體,用於資料緩衝區

NIO操作本地記憶體演示:

/**
 * 檢視直接記憶體的佔用與釋放
 */
public class BufferTest {
    private static final int BUFFER = 1024 * 1024 * 1024;//1GB

    public static void main(String[] args){
        //直接分配本地記憶體空間
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
        System.out.println("直接記憶體分配完畢,請求指示!");

        Scanner scanner = new Scanner(System.in);
        scanner.next();

        System.out.println("直接記憶體開始釋放!");
        byteBuffer = null;
        System.gc();
        scanner.next();
    }
}

執行程式,檢視程序 ,佔用1GB的記憶體

釋放後,記憶體減少1GB

2. 直接記憶體和jvm記憶體的區別

原來採用BIO的架構,在讀寫本地檔案時,我們使用的是jvm分配的記憶體,需要從使用者態切換成核心態

而NIO中的 DirectByteBuffer 直接操作 本地記憶體 操作磁碟,省去使用者態和核心態之間的切換消耗

3. 直接記憶體中OOM

直接記憶體也可能導致OutofMemoryError異常java.lang.OutOfMemoryError: Direct buffer memory

由於直接記憶體在Java堆外,因此它的大小不會直接受限於-Xmx指定的最大堆大小,但是系統記憶體是有限的,Java堆和直接記憶體的總和依然受限於作業系統能給出的最大記憶體。

直接記憶體的缺點為:

  • 分配回收成本較高
  • 不受JVM記憶體回收管理

直接記憶體大小可以通過MaxDirectMemorySize設定 (不影響元空間的大小)

如果不指定,預設與堆的最大值-Xmx引數值一致

在上面的 DirectByteBuffer 中,其底層也是通過 Unsafe 類來開闢 直接記憶體的,下面用反射的方式 直接進行分配

public class MaxDirectMemorySizeTest {
    private static final long _1MB = 1024 * 1024;

    public static void main(String[] args) throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(_1MB);
        }

    }
}

無限的分配 直接記憶體,報錯資訊:

Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at com.atguigu.java.MaxDirectMemorySizeTest.main(MaxDirectMemorySizeTest.java:20)