前言
这一节就来看看UnPooledByteBufAllocator创建ByteBuf的大致流程,对比起后边学的PooledByteBufAllocator,实在是简单得不能再简单了。
另外,本节不会再阐述不同ByteBuf的区别,有兴趣的可以参考上一节,如果对本文某些名词感到陌生,也建议回去看看。
Netty Version:4.1.6
跟进源码
由于UnPooledByteBufAllocator创建ByteBuf的过程比较简单,不像PooledByteBufAllocator那般复杂,所以下面会讲到heap、direct两种内存的分配。
另外下面讲到的方法,入口其实都是如下代码,由于中间过程很简单,所以就略了:
PooledByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
ByteBuf byteBuf = allocator.directBuffer(16);
ByteBuf byteBuf = allocator.heapBuffer(16);
分配Heap内存
UnpooledByteBufAllocator中分配heap内存是使用newHeapBuffer方法,我们找到这个方法,此处【坐标1】:
io.netty.buffer.UnpooledByteBufAllocator#newHeapBuffer
@Override
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
: new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
- initialCapacity就是初始申请的内存大小,也就是用户需要的内存大小。
- maxCapacity是指内存最大允许大小,默认传进来是Integer.MAX_VALUE。
- 如果Netty能拿到jdk底层的unsafe,就会调用UnpooledUnsafeHeapByteBuf构造。
假设现在Netty能拿不到jdk底层的unsafe对象,那就是调用UnpooledHeapByteBuf的构造方法了,跟进去瞧瞧:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, int, int)
protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
}
- 到这一步其实就不用再胡乱猜想了,就是通过new byte数据来创建、分配内存给ByteBuf。
继续跟进this:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, byte[], int, int, int)
private UnpooledHeapByteBuf(
ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {
super(maxCapacity);
if (alloc == null) {
throw new NullPointerException("alloc");
}
if (initialArray == null) {
throw new NullPointerException("initialArray");
}
if (initialArray.length > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialArray.length, maxCapacity));
}
this.alloc = alloc;
setArray(initialArray);
setIndex(readerIndex, writerIndex);
}
private void setArray(byte[] initialArray) {
array = initialArray;
tmpNioBuf = null;
}
- 其实就是保存属性,还有初始化指针偏移量。
- 之后所有的内存操作都是基于上面初始化的byte数组和指针,充分体现了heap的特性。
好了,返回最初的neHeapBuffer方法【坐标1】,看看UnpooledUnsafeHeapByteBuf构造方法:
io.netty.buffer.UnpooledUnsafeHeapByteBuf#UnpooledUnsafeHeapByteBuf
UnpooledUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(alloc, initialCapacity, maxCapacity);
}
跟进super方法:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, int, int)
protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
}
- 发现UnpooledUnsafeHeapByteBuf的构造方法其实就是原封不动的调用UnpooledHeapByteBuf的构造方法。
- 那既然它们两的构造方法都一样,那它们到底有什么不同呢?其实这个在上一节讲是否unsafe区别的时候就当道了,主要是在内存上读写时是否用底层的native方法。
分配Direct内存
UnpooledByteBufAllocator分配direct内存最终是newDirectBuffer方法创建,下面找到这个方法,此处【坐标2】:
io.netty.buffer.UnpooledByteBufAllocator#newDirectBuffer
@Override
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
ByteBuf buf = PlatformDependent.hasUnsafe() ?
UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
}
- 同样由Netty是否拿到jdk底层unsafe对象,决定是否创建unsafe类型的。
这里先跟进非unsafe的构造,即跟进UnpooledDirectByteBuf构造方法:
io.netty.buffer.UnpooledDirectByteBuf#UnpooledDirectByteBuf(io.netty.buffer.ByteBufAllocator, int, int)
protected UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(maxCapacity);
if (alloc == null) {
throw new NullPointerException("alloc");
}
if (initialCapacity < 0) {
throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
}
if (maxCapacity < 0) {
throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
}
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
}
this.alloc = alloc;
setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
}
- 重点关注最后一行,前面的都是校验、属性保存。
跟进allocateDirect方法:
java.nio.ByteBuffer#allocateDirect
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
- 可见到这一步就是创建jdk底层的DirectByteBuffer对象,而DirectByteBuffer的构建则是基于jdk的unsafe分配直接内存的。
返回,最终setByteBuffer方法保存DirectByteBuffer对象:
io.netty.buffer.UnpooledDirectByteBuf#setByteBuffer
private void setByteBuffer(ByteBuffer buffer) {
ByteBuffer oldBuffer = this.buffer;
if (oldBuffer != null) {
if (doNotFree) {
doNotFree = false;
} else {
freeDirect(oldBuffer);
}
}
this.buffer = buffer;
tmpNioBuf = null;
capacity = buffer.remaining();
}
然后ByteBuf构造完成,返回给用户。
返回到【坐标2】的newDirectBuffer方法,跟进UnsafeByteBufUtil.newUnsafeDirectByteBuf方法:
io.netty.buffer.UnsafeByteBufUtil#newUnsafeDirectByteBuf
static UnpooledUnsafeDirectByteBuf newUnsafeDirectByteBuf(
ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
if (PlatformDependent.useDirectBufferNoCleaner()) {
return new UnpooledUnsafeNoCleanerDirectByteBuf(alloc, initialCapacity, maxCapacity);
}
return new UnpooledUnsafeDirectByteBuf(alloc, initialCapacity, maxCapacity);
}
- 关于UnpooledUnsafeNoCleanerDirectByteBuf,其实就是继承了UnpooledUnsafeDirectByteBuf并腹泻了一些方法,有兴趣的可以自己看看,这里不再赘述。
- 我们应该关注UnpooledUnsafeDirectByteBuf的构造。
跟进UnpooledUnsafeDirectByteBuf构造方法:
io.netty.buffer.UnpooledUnsafeDirectByteBuf#UnpooledUnsafeDirectByteBuf(io.netty.buffer.ByteBufAllocator, int, int)
protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
// 保存属性
super(maxCapacity);
if (alloc == null) {
throw new NullPointerException("alloc");
}
if (initialCapacity < 0) {
throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
}
if (maxCapacity < 0) {
throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
}
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
}
this.alloc = alloc;
setByteBuffer(allocateDirect(initialCapacity), false);
}
- 这里的setByteBuffer和上面非unsafe的同名方法是有点不一样的的,后面会继续跟进。
跟进allocateDirect方法,最终还是通过构造jdk底层的DirectByteBuffer获取到内存:
java.nio.ByteBuffer#allocateDirect
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
返回,跟进setByteBuffer方法:
io.netty.buffer.UnpooledUnsafeDirectByteBuf#setByteBuffer
final void setByteBuffer(ByteBuffer buffer, boolean tryFree) {
if (tryFree) {
ByteBuffer oldBuffer = this.buffer;
if (oldBuffer != null) {
if (doNotFree) {
doNotFree = false;
} else {
freeDirect(oldBuffer);
}
}
}
this.buffer = buffer;
memoryAddress = PlatformDependent.directBufferAddress(buffer);
tmpNioBuf = null;
capacity = buffer.remaining();
}
可以发现与非unsafe对比,unsafe最终是存储了DirectByteBuffer对象,并计算出了内存地址memoryAddress,便于Netty使用jdk底层unsafe直接操作内存。这里的memoryAddress是个很重要的参数,之后此ByteBuf做读写时,memoryAddress都会参与到某些位运算定位内存的真实地址。
至此,UnPooledByteBufAllocator创建4种类新ByteBuf的流程就结束了。
小结
- UnPooledByteBufAllocator创建不同类型的ByteBuf,会有不同的策略。底层存储也不一样,比如heap内存实质上就是byte数组,而direct内存则是jdk底层的DirectByteBuffer对象。