一提到JVM运行的的内存划分,相信大家都会第一反应想到堆内存和栈内存。如果再分清楚一些,就是常说的运行时数据五大部分:方法区,虚拟机栈,本地方法栈,堆,程序计数器。
本文重点聊一聊直接内存(Direct Memory),直接内存并不是运行时数据区的一部分,也不是Java虚拟机规范中定义的内在区域。它通过Unsafe类的allocateMemory()方法申请分配内存,可以通过-XX:MAxDirectMemorySize指定,如果不指定 ,默认与Java堆最大值(-Xmx)一样。
说直接内存,先提一个东西,那就是NIO,至从JDK1.4中新加入了NIO,引入了一种基于通道(Channel)和缓存区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内在的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
显然,这块直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)大小又及处理器寻址空间限制。服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略直接内存,使得各个内存区域总和大于总物理内存,从而导致动态扩展时出现OUtOfMemoryError异常。
部分内存在程序运行中也被频繁地使用,而且也可能导致OOM。当直接内存发生内存溢出时,Heap Dump文件中不会看见明显的异常,如果发现OOM后Dump文件很小,而程序中使用了NIO(比如使用了Netty这类框架),那就要考虑这方面的问题了。