Java NIO direct buffer的优势在哪儿?
关注者
207被浏览
57,436登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏
HeapByteBuffer与DirectByteBuffer的区别在于HeapByteBuffer是在Java Heap上分配的,但是Java NIO在读写到相应的Channel的时候,会先将Java Heap的buffer内容拷贝至直接内存——Direct Memory。这样的话,无疑DirectByteBuffer的IO性能肯定强于使用HeapByteBuffer,它省去了临时buffer的拷贝开销,这也是为什么各个NIO框架大多使用DirectByteBuffer的原因。
具体可以参考 sun.nio.ch.IOUtil#write和sun.nio.ch.IOUtil#read方法验证:
static int write(FileDescriptor fd, ByteBuffer src, long position,
NativeDispatcher nd)
throws IOException
{
if (src instanceof DirectBuffer)
return writeFromNativeBuffer(fd, src, position, nd);
// Substitute a native buffer
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
try {
bb.put(src);
bb.flip();
// Do not update src until we see how many bytes were written
src.position(pos);
int n = writeFromNativeBuffer(fd, bb, position, nd);
if (n > 0) {
// now update src
src.position(pos + n);
}
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
————————————
static int read(FileDescriptor fd, ByteBuffer dst, long position,
NativeDispatcher nd)
throws IOException
{
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
if (dst instanceof DirectBuffer)
return readIntoNativeBuffer(fd, dst, position, nd);
// Substitute a native buffer
ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
try {
int n = readIntoNativeBuffer(fd, bb, position, nd);
bb.flip();
if (n > 0)
dst.put(bb);
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
---------------------------------------------------------------------
为什么要采用C Heap的buffer通过系统调用与kernel交互?我这里简单的说下几个原因(以linux平台为例),欢迎大家补充:
- 底层通过write、read、pwrite,pread函数进行系统调用时,需要传入buffer的起始地址和buffer count作为参数。具体参见:write(2): to file descriptor,read(2): read from file descriptor,pread(2) - Linux man page,pwrite(2) - Linux man page。如果使用java heap的话,我们知道jvm中buffer往往以byte[] 的形式存在,这是一个特殊的对象,由于java heap GC的存在,这里对象在堆中的位置往往会发生移动,移动后我们传入系统函数的地址参数就不是真正的buffer地址了,这样的话无论读写都会发生出错。而C Heap仅仅受Full GC的影响,相对来说地址稳定。
- JVM规范中没有要求Java的byte[]必须是连续的内存空间,它往往受宿主语言的类型约束;而C Heap中我们分配的虚拟地址空间是可以连续的,而上述的系统调用要求我们使用连续的地址空间作为buffer。