[转]http://littcai.iteye.com/blog/300581
这两种类型的ByteBuffer相信大家都知道,但是两者的区别在什么地方呢?在不同的环境下采用哪种类型的ByteBuffer会更有效率呢?
先解释一下两者的区别:
Non-direct ByteBuffer内存是分配在堆上的,直接由Java虚拟机负责垃圾收集,你可以把它想象成一个字节数组的包装类,如下伪码所示:
HeapByteBuffer extends ByteBuffer {
byte[] content;
int position, limit, capacity;
......
}
而Direct ByteBuffer是通过JNI在Java虚拟机外的内存中分配了一块(所以即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的Direct ByteBuffer),该内存块并不直接由Java虚拟机负责垃圾收集,但是在Direct ByteBuffer包装类被回收时,会通过Java Reference机制来释放该内存块。如下伪码所示:
DirectByteBuffer extends ByteBuffer {
long address;
int position, limit, capacity;
protected void finalize() throws Throwable{
//释放内存块,该段代码仅仅用于演示,真正的Direct ByteBuffer并不是通过finalize来释放的
releaseAddress();
......
}
......
}
我相信大部分朋友们对上面的区别都应该很了解,那么还有什么其他的区别呢?嘿嘿,让我们稍微深入一点,翻到sun.nio.ch.IOUtil.java,绝大部分Channel类都是通过这个工具类和外界进行通讯的,如FileChannel/SocketChannel等等。我简单的用伪码把write方法给表达出来(read方法也差不多,就不多做说明了)
int write(ByteBuffer src, ......) {
if (src instanceof DirectBuffer)
return writeFromNativeBuffer(...);
ByteBuffer direct = getTemporaryDirectBuffer(src);
writeFromNativeBuffer(direct,......);
updatePosition(src);
releaseTemporaryDirectBuffer(direct);
}
是的,在发送和接收前会把Non-direct ByteBuffer转换为Direct ByteBuffer,然后再进行相关的操作,最后更新原始ByteBuffer的position。这意味着什么?假设我们要从网络中读入一段数据,再把这段数据发送出去的话,采用Non-direct ByteBuffer的流程是这样的:
网络 --> 临时的Direct ByteBuffer --> 应用 Non-direct ByteBuffer --> 临时的Direct ByteBuffer --> 网络
而采用Direct ByteBuffer的流程是这样的:
网络 --> 应用 Direct ByteBuffer --> 网络
可以看到,除开构造和析构临时Direct ByteBuffer的时间外,起码还能节约两次内存拷贝的时间。那么是否在任何情况下都采用Direct Buffer呢?
不是。对于大部分应用而言,两次内存拷贝的时间几乎可以忽略不计,而构造和析构Direct Buffer的时间却相对较长。在JVM的实现当中,某些方法会缓存一部分临时Direct ByteBuffer,意味着如果采用Direct ByteBuffer仅仅能节约掉两次内存拷贝的时间,而无法节约构造和析构的时间。就用Sun的实现来说,write(ByteBuffer)和read(ByteBuffer)方法都会缓存临时Direct ByteBuffer,而write(ByteBuffer[])和read(ByteBuffer[])每次都生成新的临时Direct ByteBuffer。
根据这些区别,我会提出如下的建议:
-
如果你做中小规模的应用(在这里,应用大小是按照使用ByteBuffer的次数和规模来做划分的),而且并不在乎这该死的细节问题,请选择Non-direct ByteBuffer
-
如果采用Direct ByteBuffer后性能并没有出现你所期待的变化,请选择Non-direct ByteBuffer
-
如果没有Direct ByteBuffer Pool,尽量不要使用Direct ByteBuffer
-
除非你确定该ByteBuffer会长时间存在,并且和外界有频繁交互,可采用Direct ByteBuffer
-
如果采用Non-direct ByteBuffer,那么采用非聚集(gather)的write/read(ByteBuffer)效果反而可能超出聚集的write/read(ByteBuffer[]),因为聚集的write/read的临时Direct ByteBuffer是非缓存的
基本上,采用Non-direct ByteBuffer总是对的!因为内存拷贝需要的开销对大部分应用而言都可以忽略不计。不过我做的是大规模的网络并发框架,因此对这些细节问题还是有必要有深入认识的,并且根据这些细节来调节自己的Buffer继承体系(再次抱怨,ByteBuffer无法扩展实在是一个非常非常非常费解的设计)
注:前面提到的“即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的Direct ByteBuffer”中的可能是指可以通过-XX:MaxDirectMemorySize=<size>来指定Direct ByteBuffer实例最多可以使用的内存总数。如指定-XX:MaxDirectMemorySize=1024,则系统中所有存活的Direct ByteBuffer总内存数不能超过1024字节。
分享到:
相关推荐
【IT十八掌徐培成】Java基础第26天-05.ByteBuffer-mark-pos-limit-cap-flip.zip
dena-bytebuffer
ios-byteBuffer [![CI状态]( Lee / ios-byteBuffer.svg?style = flat)]( Lee / ios-byteBuffer ) 用法 #分配 ByteBuffer *buffer = [ByteBuffer initWithOrder: ByteOrderLittleEndian]; #输入数据 - ( ...
├─(5) 第1章_05_bytebuffer-基本使用.mp4 ├─(6) 第1章_06_bytebuffer-内部结构.mp4 ├─(7) 第1章_07_bytebuffer-方法演示1.mp4 ├─(8) 第1章_08_bytebuffer-方法演示2.mp4 ├─(9) 第1章_09_bytebuffer-方法...
ByteBuffer-scanner-Memor-Hex 宝
NULL 博文链接:https://chinaestone.iteye.com/blog/468138
http://docs.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html 主要用于各种网络协议的组包具体用法可以点上面的网址功能和jAVA的一样
仿安卓ByteBuffer 完美组包、拆包
Java-NIO-Programming-Cookbook英文版和配套源代码。有一个使用DirectByteBuffer和Non-Direct Buffer文件读取速度的对比实验程序和测试结果。
使用nio byteBuffer 实现按行读取文件(大文件) 在window/linux/macOS上均测试通过 对于中文乱码也已处理成功 完整注释,可随需求更改 有问题请邮件:mly610865580@126.com
jdk api-ServerSocketChannel、Selector、ByteBuffer结合实现网络报文间的通讯
Android
易语言汇编版ByteBuffer源码主要用于各种网络协议的组包 具体用法可以点上面的网址 功能和jAVA的一样
主要解决从流中获取数据,缓存,拆解,可用于TCP粘包问题
易语言汇编版ByteBuffer源码。主要用于各种网络协议的组包 具体用法可以点上面的网址 功能和jAVA的一样。@10371178。Tags:易语言汇编版ByteBuffer源码。
本文实例讲述了Android在JNI中使用ByteBuffer的方法。分享给大家供大家参考。具体如下: 一、ByteBuffer 定义 在NIO中,数据的读写操作始终是与缓冲区相关联的(读取时信道(SocketChannel)将数据读入缓冲区,写入时...
在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的同步(blocking)API。...从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供异步(non-blocking)IO操作的API被引入。本文对其进行深入的介绍。
protobuf+long+bytebuffer,利用protobuf.js实现编解码 所需的三个js库
java api之ByteBuffer基础、应用场景、实战讲解 文档中有丰富的例子代码实现