1.1.什么是直接內存(Derect Memory)
在內存模型最開始的章節中,我們畫出了JVM的內存模型,里面並不包含直接內存,也就是說這塊內存區域並不是JVM運行時數據區的一部分,但它卻會被頻繁的使用,原因是NIO這個包。
NIO(New input/output)是JDK1.4中新加入的類,引入了一種基於通道(channel)和緩沖區(buffer)的I/O方式,它可以使用Native函數庫直接分配堆外內存,然后通過堆上的DirectByteBuffer對象對這塊內存進行引用和操作。


可以看出,直接內存的大小並不受到java堆大小的限制,甚至不受到JVM進程內存大小的限制。它只受限於本機總內存(RAM及SWAP區或者分頁文件)大小以及處理器尋址空間的限制(最常見的就是32位/64位CPU的最大尋址空間限制不同)。
1.2.直接內存的OufOfMemoryError
直接內存出現OutOfMemoryError的原因是對該區域進行內存分配時,其內存與其他內存加起來超過最大物理內存限制(包括物理的和操作系統級的限制),從而導致OutOfMemoryError。另外,若我們通過參數“-XX:MaxDirectMemorySize”指定了直接內存的最大值,其超過指定的最大值時,也會拋出內存溢出異常。
1 /** 2 * jvm直接內存溢出 3 * JVM參數:-Xmx20M -XX:MaxDirectMemorySize=10M 4 * Created by chenjunyi on 2018/4/26. 5 */ 6 public class DirectMemoryOOM { 7 8 private static final int _1MB = 1024 * 1024; 9 10 public static void main(String[] args) throws IllegalAccessException { 11 //通過反射獲取Unsafe類並通過其分配直接內存 12 Field unsafeField = Unsafe.class.getDeclaredFields()[0]; 13 unsafeField.setAccessible(true); 14 Unsafe unsafe = (Unsafe) unsafeField.get(null); 15 while (true) { 16 unsafe.allocateMemory(_1MB); 17 } 18 } 19 20 }
結果如下,可以看出,其拋出的內存溢出異常並沒有指定是JVM那一塊數據區域:
1 Exception in thread "main" java.lang.OutOfMemoryError 2 at sun.misc.Unsafe.allocateMemory(Native Method) 3 at com.manayi.study.jvm.chapter2._07_DirectMemoryOOM.main(_07_DirectMemoryOOM.java:22)
上面的例子有點特殊,因為我們使用到了Unsafe這個類(這個類為什么通過反射進行獲取先不討論),它的allocateMemory方法能夠直接從堆外內存中申請內存(類比於c的malloc函數)。不同於DirectByteBuffer的內存分配方式(先計算是否有足夠的可用內存再決定是手動拋異常還是向操作系統申請分配內存),Unsafe是直接向操作系統申請分配內存,若未申請到則拋異常。