线上应用经常会出现OutOfMemoryError错误,档案引起原因多种多样,包括堆内存溢出(Java Heap Space)、方法区持久代内存溢出(Permanet Space)以及本文需要说道的Native操作本地内存出现的内存溢出问题。要说明这个问题,就先要说明Native函数库是怎样操作内存空间的。
NIO中的Native函数库
在JDK1.4中新加入了NIO类,引入一种基于渠道与缓冲区的I/O方式,它可以通过本机Native函数库直接分配本机内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。所以由Native函数库引起的OOM大多是由于直接使用了本地直接内存(Direct Memory)。
NIO之所以引入该机制,这样能在一些场景中显著提高性能,因为避免了在Java对和本机堆中来回复制数据。显然本机直接内存的分配不会受到Java堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括SWAP区或者Windows虚拟内存)的限制的,一般服务器管理员配置JVM参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError异常。
关于如何定位OOM异常,请参看我的另一篇文章《一次线上OOM排查经过》。
设定JVM参数
按照jvm规范,本地直接内存的最大值按以下顺序设定: