相关文章:
直接内存 (Direct Memory) 并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域,而是可以使用 Native 函数库直接分配的堆外内存,也可称为本地内存 (Native Memory)
一、相关定义
-
在 JDK4 中新加入了 NIO (New Input/Output) 类,引入了一种基于通道 (Channel) 与缓冲区 (Buffer) 的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用来进行操作。这样在一些场景中可以显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据
-
操作系统在 I/O 操作过程中,如果数据存储在堆内存中,需要先从堆内存拷贝到直接内存,然后再输入到 I/O 设备
-
而如果数据直接存储在直接内存中,则可以直接输入到 I/O 设备,避免了从堆内存到直接内存的数据拷贝
-
-
显然,本机直接内存的分配不会受到 Java 堆大小的限制,但还是会受到本机总内存 (包括 RAM 以及 SWAP 区或者分页文件) 大小以及处理器寻址空间的限制。如果各个内存区域的总和大于物理内存的限制 (包括物理的和操作系统级的限制),则会导致动态扩展时出现 OutOfMemoryError 异常
-
我们可以通过
-XX:MaxDirectMemorySize
参数来设置最大直接内存大小
二、DirectBuffer
-
由上可知,通过 DirectBuffer 我们可以直接访问直接内存,其继承自 ByteBuffer,和其他不同的 ByteBuffer 不一样
-
普通的 ByteBuffer 对象在 Java 堆上分配内存,其最大内存受到 Java 堆内存大小的限制;而 DirectBuffer 对象直接在直接内存上分配,并不占用堆空间,受到本机物理内存大小的限制
-
在读取速度上,DirectBuffer 对象比普通的 ByteBuffer 对象要快;但它的创建销毁速度要比普通的 ByteBuffer 对象要慢
三、举例说明
-
测试访问速度
public static void main(String[] args) { long start1 = System.currentTimeMillis(); ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(1024); IntStream.range(0, 1000000).forEach((i) -> { IntStream.range(0, 100).forEach(directByteBuffer::putInt); IntStream.range(0, 100).forEach(directByteBuffer::getInt); directByteBuffer.clear(); }); long end1 = System.currentTimeMillis(); System.out.println("directByteBuffer " + (end1 - start1) + "ms"); // directByteBuffer 237ms long start2 = System.currentTimeMillis(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); IntStream.range(0, 1000000).forEach((i) -> { IntStream.range(0, 100).forEach(byteBuffer::putInt); IntStream.range(0, 100).forEach(byteBuffer::getInt); byteBuffer.clear(); }); long end2 = System.currentTimeMillis(); System.out.println("byteBuffer " + (end2 - start2) + "ms"); // byteBuffer 356ms }
- 如上所示,对 directByteBuffer 和 byteBuffer 分别进行了大量的读写操作,结果显示 directByteBuffer 读取的速度要比 byteBuffer 的要快
-
测试创建销毁速度
public static void main(String[] args) { long start1 = System.currentTimeMillis(); IntStream.range(0, 1000000).forEach((i) -> { ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(1024); }); long end1 = System.currentTimeMillis(); System.out.println("directByteBuffer " + (end1 - start1) + "ms"); // directByteBuffer 1281ms long start2 = System.currentTimeMillis(); IntStream.range(0, 1000000).forEach((i) -> { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); }); long end2 = System.currentTimeMillis(); System.out.println("byteBuffer " + (end2 - start2) + "ms"); // byteBuffer 380ms }
- 如上所示,分别创建了大量的 directByteBuffer 和 byteBuffer 对象,结果显示 directByteBuffer 创建销毁的速度要比 byteBuffer 的要慢
四、归纳总结
-
直接内存大小受到本机总内存大小以及处理器寻址空间的限制
-
可以通过使用
-XX:MaxDirectMemorySize
参数来设置最大直接内存大小 -
DirectBuffer 对象的读取速度要比普通的 ByteBuffer 对象要快,创建销毁速度要比普通的 ByteBuffer 对象要慢