一、JVM直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
- 本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制
- 直接内存也可以由 -XX:MaxDirectMemorySize 指定
- 直接内存申请空间耗费更高的性能
- 直接内存IO读写的性能要优于普通的堆内存
当我们的需要频繁访问大的内存而不是申请和释放空间时,通过使用直接内存可以提高性能。
二、直接内存溢出测试
测试代码如下,运行时添加参数 -Xmx20M -XX:MaxDirectMemorySize=10M 设置降低直接内存的空间来加快异常的抛出
package test_DirectMemoryOOM;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class DirectrMemoryOOM {
private static final int _1M = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
@SuppressWarnings("restriction")
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while(true) {
unsafe.allocateMemory(_1M);
}
}
}
对于在eclipse上,使用sun.misc.Unsafe会出现找不到类的报错,这是出于安全考虑做的限制。可以通过以下方法解决:
Project > properties > Java Compilee > Errors/Warnings :
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at test_DirectMemoryOOM.DirectrMemoryOOM.main(DirectrMemoryOOM.java:15)
不断申请1M的空间,最终会耗尽内存抛出 OutOfMemoryError 异常。
三、注意
由于申请直接内存不由虚拟机管理,所以由此导致的 OOM 是不会在 Heap Dump 文件中看出明显的异常。当 OOM 后发现 Dump 文件很小同时程序直接或间接使用了 NIO ,就可以考虑一下这方面的原因。
关于 Heap Dump 的内容可以在 JVM学习笔记——jhat的使用 了解