10. 直接記憶體
阿新 • • 發佈:2020-12-04
我們知道 在jdk8 之後,Hotspot 將 方法區的實現改為元空間,直接使用本地記憶體,下面就來詳細瞭解一下
1. 直接記憶體的概述
- 不是虛擬機器執行時資料區的一部分,也不是《Java虛擬機器規範》中定義的記憶體區域。
- 直接記憶體是在Java堆外的、直接向系統申請的記憶體區間。
- 起源於NIO,通過存在堆中的DirectByteBuffer操作Native記憶體
- 通常,訪問直接記憶體的速度會優於Java堆。即讀寫效能高。因此出於效能考慮,讀寫頻繁的場合可能會考慮使用直接記憶體。
- 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)