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是直接向操作系统申请分配内存,若未申请到则抛异常。