天天看點

JVM 直接記憶體的配置設定與釋放詳解和底層實作

直接記憶體

定義:

  • 常見于 NIO 操作時,用于資料緩沖區
  • 配置設定回收成本較高,但讀寫性能高
  • 不受 JVM 記憶體回收管理
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class memory_overflow {
    static int _100Mb = 1024 * 1024 * 100;
    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
    }
}
           

直接記憶體配置設定,每次記憶體配置設定100m,一直配置設定空間,運作代碼會抛出異常 java.lang.OutOfMemoryError: Direct buffer memory , 表示直接記憶體不足。

記憶體的占用 釋放原理

在這裡我們運作一段代碼,給添加一個g的記憶體,之後再進行釋放記憶體,在任務管理器當中進行檢視記憶體的占用情況。使用以下代碼段進行測試

import java.io.IOException;
import java.nio.ByteBuffer;

public class GC_memory {
    static int _1Gb = 1024 * 1024 * 1024;
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println("配置設定完畢...");
        System.in.read();
        System.out.println("開始釋放...");
        byteBuffer = null;
        System.gc(); // 顯式的垃圾回收,Full GC
        System.in.read();
    }
}
           

在按壓第一次回車後配置設定記憶體,再一次按壓回車後将記憶體釋放掉。

JVM 直接記憶體的配置設定與釋放詳解和底層實作

釋放原理的底層實作

直接記憶體配置設定的底層原理:Unsafe ;

使用了 Unsafe 對象完成直接記憶體的配置設定回收,并且回收需要主動調用 freeMemory 方法

ByteBuffer 的實作類内部,使用了 Cleaner (虛引用)來監測 ByteBuffer 對象,一旦

ByteBuffer 對象被垃圾回收,那麼就會由 ReferenceHandler 線程通過 Cleaner 的 clean 方法調用 freeMemory 來釋放直接記憶體

使用以下代碼段進行示範

package direct;

import sun.misc.Unsafe;
import java.io.IOException;
import java.lang.reflect.Field;

public class GC_memory_bottom {
    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        Unsafe unsafe = getUnsafe();
        // 配置設定記憶體
        long base = unsafe.allocateMemory(_1Gb);
        unsafe.setMemory(base, _1Gb, (byte) 0);
        System.in.read();
        // 釋放記憶體
        unsafe.freeMemory(base);
        System.in.read();
    }
    
    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);
            return unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
           

執行代碼,在任務管理器當中檢視記憶體占用,再配置設定了1g的記憶體後,會上升,記憶體釋放後,又會下降下來。

JVM 直接記憶體的配置設定與釋放詳解和底層實作

性能調優

禁用顯示的垃圾回收機制。使用參數 -XX:+DisableExplicitGC 禁用之後直接記憶體的回收會受到影響,在 記憶體的占用 釋放原理 這裡的代碼段進行添加參數。進行測試:

在進行記憶體釋放的時候通常要采用unsafe對象進行釋放

JVM 直接記憶體的配置設定與釋放詳解和底層實作