原因
写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
ByteBuffer.allocate(capability)是分配JVM堆内存,属于GC管辖范围,由于需要拷贝速度相对较慢。
ByteBuffer.allocteDirect(capability)是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。
但是如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,程序也就直接崩溃了。
代码演示
VM配置
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
第一步
选择 运行 -> Edit Configurations 设置配置如图
第二步
代码
import java.nio.ByteBuffer;
public class DirectBufferMemoryDemo {
public static void main(String[] args) {
System.out.println("查看配置的本地内存maxDirectMemory:"+(sun.misc.VM.maxDirectMemory() / (double)1024 / 1024) +"MB");
//停顿一下方便看效果
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//-XX:MaxDirectMemorySize=5m 本地内存配置的是5MB,这里实际使用的是6MB
ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
运行结果:
以上演示也就是我们常说的直接内存溢出的原因。