UnpooledHeapByteBuf 是基于堆内存进行内存分配的字节缓冲区,它没有基于对象池技术实现。在每次 I/O 的读写都会创建一个新的 UnpooledHeapByteBuf,频繁进行大块内存的分配和回收对性能会造成一定影响,但是相比于堆外内存的申请和释放,它的成本会比较低一点。
该类里面的成员变量如下:
它里面定义了一个 ByteBufAllocator 类型的 alloc,用来对 UnpooledHeapByteBuf 的内存分配。定义了一个 byte[] 数组的 array 作为一个缓冲区。tmpNioBuf 的类型是 ByteBuffer 的,它是用来实现 Netty 的 ByteBuf 到 JDK NIO 的 ByteBuffer 的转换。
从前面的章节中,我们了解到 Netty 的 ByteBuf 和 JDK NIO 的 ByteBuffer 的重要区别是 JDK 的 ByteBuffer 不能自动扩容,而在 Netty 中它可以自动扩容。UnpooledHeapByteBuf 的扩容方法为 capacity,具体的源码如下:
public ByteBuf capacity(int newCapacity) {
checkNewCapacity(newCapacity);
int oldCapacity = array.length;
byte[] oldArray = array;
if (newCapacity > oldCapacity) {
byte[] newArray = allocateArray(newCapacity);
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
setArray(newArray);
freeArray(oldArray);
} else if (newCapacity < oldCapacity) {
byte[] newArray = allocateArray(newCapacity);
int readerIndex = readerIndex();
if (readerIndex < newCapacity) {
int writerIndex = writerIndex();
if (writerIndex > newCapacity) {
writerIndex(writerIndex = newCapacity);
}
System.arraycopy(oldArray, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
} else {
setIndex(newCapacity, newCapacity);
}
setArray(newArray);
freeArray(oldArray);
}
return this;
}
具体的逻辑分析如下
字节数组复制的方法是 setBytes。这边我们具体了解一下 public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length)
方法。它的源代码如下:
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
checkSrcIndex(index, length, srcIndex, src.length);
System.arraycopy(src, srcIndex, array, index, length);
return this;
}
它会先进行合法性校验,校验 index 和 length 的值,如果它们小于 0,就会抛出异常。然后对两个值的和与它的缓冲区容量比较,如果大于缓冲区容量,也会抛出异常。如果校验通过就会调用系统的 System.arraycopy 方法对数组进行复制。
ByteBuf 中以 get 和 set 开头的读写缓冲区的方法并不会修改读写索引。
从前面的章节中,我们了解到 ByteBuf 基于 byte 数组实现,NIO 的 ByteBuffer 提供了 wrap 方法,可以将 byte 数组转换成 Bytebuffer 对象。它的源代码如下:
UnpooledHeapByteBuf 类
@Override
public ByteBuffer nioBuffer(int index, int length) {
ensureAccessible();
return ByteBuffer.wrap(array, index, length).slice();
}
ByteBuffer类
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
它调用了 ByteBuffer 的 slice 方法,用来创建一个新的字节数组缓冲区,和原来的缓冲区是隔离开的。
本章我们分析了 netty 的 UnpooledHeapByteBuf 源码,它是基于堆内存进行操作的。分析了它的成员变量,扩容复制等机制,对 Netty 的 ByteBuf 的了解更进一步。