在调用fileChannle.write(ByteBuffer[] src)底层是怎么实现的?是循环写?Direct和No-Direct有什么区变?
下面结合源码来说明:
static long write(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd) throws IOException {
int nextWithRemaining = remaining(bufs);
// if all bufs are empty we should return immediately
if (nextWithRemaining < 0)
return 0;
// If some bufs are empty we should skip them
if (nextWithRemaining > 0)
bufs = skipBufs(bufs, nextWithRemaining);
int numBufs = bufs.length;
int bytesReadyToWrite = 0;
// Create shadow to ensure DirectByteBuffers are used
// 这里可以看出使用DirectBuffer要节省一次copy动作,对大数据量来说是相当可观的
ByteBuffer[] shadow = new ByteBuffer[numBufs];
for (int i = 0; i < numBufs; i++) {
if (!(bufs[i] instanceof DirectBuffer)) {
int pos = bufs[i].position();
int lim = bufs[i].limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
ByteBuffer bb = ByteBuffer.allocateDirect(rem);
shadow[i] = bb;
// Leave slow buffer position untouched; it will be updated
// after we see how many bytes were really written out
bb.put(bufs[i]);
bufs[i].position(pos);
bb.flip();
} else {
shadow[i] = bufs[i];
}
}
IOVecWrapper vec = null;
long bytesWritten = 0;
try {
// Create a native iovec array
vec = new IOVecWrapper(numBufs);
// Fill in the iovec array with appropriate data
//这里将DirectBuffer数据转化成IOVec的结构体,此结构体是系统调用writev的参数
for (int i = 0; i < numBufs; i++) {
ByteBuffer nextBuffer = shadow[i];
// put in the buffer addresses
long pos = nextBuffer.position();
long len = nextBuffer.limit() - pos;
bytesReadyToWrite += len;
vec.putBase(i, ((DirectBuffer) nextBuffer).address() + pos);
vec.putLen(i, len);
}
// Invoke native call to fill the buffers
// jni系统调用writev, 此函数作用是聚集写,所以在这里没有应用层的循环写
bytesWritten = nd.writev(fd, vec.address, numBufs);
} finally {
vec.free();
}
long returnVal = bytesWritten;
// Notify the buffers how many bytes were taken
// 按照写入的字节数,更新原始的ByteBuffers
for (int i = 0; i < numBufs; i++) {
ByteBuffer nextBuffer = bufs[i];
int pos = nextBuffer.position();
int lim = nextBuffer.limit();
assert (pos <= lim);
int len = (pos <= lim ? lim - pos : lim);
if (bytesWritten >= len) {
bytesWritten -= len;
int newPosition = pos + len;
nextBuffer.position(newPosition);
} else { // Buffers not completely filled
if (bytesWritten > 0) {
assert (pos + bytesWritten < (long) Integer.MAX_VALUE);
int newPosition = (int) (pos + bytesWritten);
nextBuffer.position(newPosition);
}
break;
}
}
return returnVal;
}