java 直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。
在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
显然,本机直接内存的分配不会受到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括RAM及SWAP区或者分页文件)的大小及处理器寻址空间的限制。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。
NIO的Buffer提供一个可以直接访问系统物理内存的类——DirectBuffer。DirectBuffer类继承自ByteBuffer,但和普通的ByteBuffer不同。普通的ByteBuffer仍在JVM堆上分配内存,其最大内存受到最大堆内存的 限制。而DirectBuffer直接分配在物理内存中,并不占用堆空间。在访问普通的ByteBuffer时,系统总是会使用一个“内核缓冲区”进行操作。而DirectBuffer所处的位置,就相当于这个“内核缓冲区”。因此,使用DirectBuffer是一种更加接近内存底层的方法,所以它的速度比普通的ByteBuffer更快。
申请DirectBuffer代码如下:
ByteBuffer.allocateDirect();
下面,使用DirectBuffer和普通的ByteBuffer进行一段性能测试。
- 使用DirectBuffer:
long start = System.currentTimeMillis();
ByteBuffer buffer = ByteBuffer.allocateDirect(500);//分配500个字节的DirectBuffer
for (int i = 0; i < 100000; i ++) {
for (int j = 0; j < 99; j ++) {
buffer.putInt(j); //向DirectBuffer写入数据
}
buffer.flip();
for (int j = 0; j < 99; j ++) {
buffer.get(); //从DirectBuffer中读取数据
}
buffer.clear();
}
System.out.println("DirectBuffer use : " + ( System.currentTimeMillis() - start ) + "ms");
执行结果:
DirectBuffer use : 20ms
- 使用ByteBuffer:
long start = System.currentTimeMillis();
ByteBuffer buffer = ByteBuffer.allocate(500);//分配500个字节的ByteBuffer
for (int i = 0; i < 100000; i ++) {
for (int j = 0; j < 99; j ++) {
buffer.putInt(j); //向DirectBuffer写入数据
}
buffer.flip();
for (int j = 0; j < 99; j ++) {
buffer.get(); //从DirectBuffer中读取数据
}
buffer.clear();
}
System.out.println("ByteBuffer use : " + ( System.currentTimeMillis() - start ) + "ms");
执行结果:
ByteBuffer use : 33ms
以后两段测试代码分别使用了DirectBuffer和堆上的ByteBuffer,并进行了大量的读写访问。测试结果是DirectBuffer相对耗时20ms,而ByteBuffer相应耗时33ms.虽然都很快,但从比例上来说,DirectBuffer接近快了一倍。如果把外层的循环次数由10万改为100万,DirectBuffer use : 105ms,而ByteBuffer use : 225ms,快了一倍多。
不过,虽然有访问速度上的优势,但是在创建和销毁DirectBuffer的花费却远比ByteBuffer高。
for (int i = 0 ;i < 20000; i ++) {
ByteBuffer b = ByteBuffer.allocateDirect(1000);
}
for (int i = 0 ;i < 20000; i ++) {
ByteBuffer b = ByteBuffer.allocate(1000);
}
上面的两个for循环表示分别请求每种类型的Buffer 20M,设置运行时参数-XX:MaxDirectMemorySize=10M -Xmx10M,运行以下代码,使用DirectBuffer的代码段相对耗时297ms,而使用ByteBuffer的相对耗时仅15ms。因此可知,频繁的创建和销毁DirectBuffer远远大于在堆上分配内存空间。
DirectBuffer的读写操作比普通Buffer快,但它的创建、销毁却比普通Buffer慢。
更多推荐
所有评论(0)