溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

什么是JVM直接內存

發(fā)布時間:2021-10-11 17:35:59 來源:億速云 閱讀:181 作者:iii 欄目:編程語言

本篇內容介紹了“什么是JVM直接內存”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

JDK7和JDK8的內存結構對比

什么是JVM直接內存

從上面的圖中可以看到Java8相比Java7來講將方法區(qū)的實現,從非堆空間(其實邏輯與堆相連,所屬于運行時數據區(qū)內部)遷移到了本地內存中,不會造成FullGC過多的壓力以及與老年代的耦合度過高的問題,減少FullGC的掃描范圍,從而改為手動去回收機制(也可以自動回收需要配置調整)。 之前的文章里面介紹了JVM的運行時數據區(qū)的相關介紹,一直對直接內存的研究和學習較少,現在我們就開始介紹一下直接內存的分配方式以及回收方式。

直接內存的定義

1. 常見于NIO操作時,用于數據緩沖區(qū)(ByteBuffer.allocate),當然也可以采用Unsafe類進行native方法運作進行申請直接內存。

2. 分配回收成本較高(屬于操作系統(tǒng)內存),但讀寫性能高。(因為直接內存不需要經過JVM解釋器進行地址映射轉換到系統(tǒng)真正內存,故此讀寫速度會比堆內存在快很多,但是申請和回收機制角度而言復雜,因為屬于直接由操作系統(tǒng)進行管理,而非JVM直接進行管理。

3. 不受JVM內存回收管理(依舊存在內存溢出的問題),此外受限制與操作系統(tǒng)物理內存,以及我們可以手動設置它的閾值(MaxDirectMemory),默認情況下來講直接內存幾乎沒有限制(當沒有超出物理內存的控制而言)

4. jvm的內存分配與回收是自動的,不需要手動調用任何的方法,但是直接內存需要我們手動調用方法。

直接內存基本使用(IO操作舉例)

  1. Java堆內存:

什么是JVM直接內存

  1. 使用直接內存后,可以減少步驟:

什么是JVM直接內存

可以看出來,直接內存無需進行與java堆內存進行映射和轉換,可以直接去操作系統(tǒng)內存。

直接內存導致的內存溢出問題

書寫程序:每次都分配直接內存,直到內存溢出

public class Test1 {
    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);
        }
    }
}

測試結果:

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at pers.zhb.test.Test1.main(Test1.java:15)

直接內存的分配與回收(底層通過Unsafe對象管理)

直接內存的分配與回收

運行程序前:

什么是JVM直接內存

直接內存的分配與釋放程序:

public class Test1 {
    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();
    }
}

分配直接內存后: 什么是JVM直接內存

可以看出來相關的多出一個java的進程,并且內存占用1037M,所以也證實了我們的猜測和預想。

在IDEA的控制臺點擊回車對內存進行釋放:

什么是JVM直接內存

可以看到恢復到了原始的狀態(tài)。

控制臺打印出分配與回收的提示:

分配完畢
開始釋放
Process finished with exit code 0

其中System.gc() ,強制執(zhí)行FullGC,回收掉byteBuffer對象

至此,我們的推測全部驗證通過,直接內存會采用另外一個進程去存儲相關的系統(tǒng)內存區(qū)域,并且不屬于jvm內存之內的管理。

Unsafe實現對直接內存的分配與回收:
public class Test1 {
    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(){
        Field field= null;
        try {
            field = Unsafe.class.getDeclaredField("theUnsafe");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        field.setAccessible(true);
        Unsafe unsafe= null;
        try {
            unsafe = (Unsafe)field.get(null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return unsafe;
    }
}

jvm的內存分配與回收是自動的,不需要手動調用任何的方法,但是直接內存需要我們手動調用方法。

ByteBuffer源碼

ByteBuffer (屬于工具方法、工廠方法)

ByteBuffer byteBuffer= ByteBuffer.allocateDirect(_1Gb);

allocateDirect(分配內存方法)

public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}

DirectByteBuffer(直接內存的對象)

DirectByteBuffer(int cap) {
        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
 }

底層用到的依舊是unsafe對象

優(yōu)缺點

優(yōu)點

  • 減少了垃圾回收

堆外內存是直接受操作系統(tǒng)管理(不是JVM)。這樣做能保持一個較小的堆內內存,以減少垃圾收集對應用的影響。

  • 提升IO速度

堆內內存由JVM管理,屬于“用戶態(tài)”;而堆外內存由OS管理,屬于“內核態(tài)”。如果從堆內向磁盤寫數據時,數據會被先復制到堆外內存,即內核緩沖區(qū),然后再由OS寫入磁盤,使用堆外內存避免了這個操作。

缺點

  • 沒有JVM協(xié)助管理內存,需要我們自己手動來管理堆外內存,防止內存溢出同時也為了避免一直有FULL GC,最終導致物理內存被耗完。

我們會指定直接內存的最大值,通過-XX:MaxDirectMemorySize來指定,當達到閾值的時候,調用system.gc來進行一次full gc,把那些沒有被使用的直接內存回收掉。

  • 此外,分配直接內存和回收內存的性能和速度也比較復雜,所以成本也會很高,特別是回收需要FullGC、分配內存的時候,需要切換一次用戶態(tài)到系統(tǒng)態(tài)的操作,分配直接內存,從而用用戶態(tài)的句柄來引用系統(tǒng)態(tài)的內存空間。

“什么是JVM直接內存”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI