我们从两个角度分析UnpooledByteBufAllocator:
1、heap内存的分配
2、direct内存的分配
由于unpooled就是自己去操作底层api去分配内存,实现起来比较简单。
一、heap内存的分配
上一篇文章其实在最后一部分我们分析过了,heap的的内存分配和读取都是在array数组上面。分配的堆内存源码就是下面UnpooledByteBufAllocator的这一段:
@Override
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
: new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
参考上一篇文章第三部分看可以了
二、direct内存的分配
和heap不同,direct内存分配放在buffer里面,然后unsafe的direct buffer是通过底层的unsafe类去操作的。(其实看源码到最终,direct的buffer都是通过unsafe去操作的,也就是jdk底层的allocateDirect最终还是调用到unsafe,这里)
从UnpooledByteBufAllocator分配direct内存的这个源码开始看起:
@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);
}
我们先看unsafe吧,从UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) 这里进去:
static UnpooledUnsafeDirectByteBuf newUnsafeDirectByteBuf(
ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
if (PlatformDependent.useDirectBufferNoCleaner()) {
return new UnpooledUnsafeNoCleanerDirectByteBuf(alloc, initialCapacity, maxCapacity);
}
return new UnpooledUnsafeDirectByteBuf(alloc, initialCapacity, maxCapacity);
}
不管nocleaner先,从最后UnpooledUnsafeDirectByteBuf这个函数进去:
/**
* Creates a new direct buffer.
*
* @param initialCapacity the initial capacity of the underlying direct buffer
* @param maxCapacity the maximum capacity of the underlying direct buffer
*/
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);
}
最后这个allocateDirect(initialCapacity):
/**
* Allocate a new direct {@link ByteBuffer} with the given initialCapacity.
*/
protected ByteBuffer allocateDirect(int initialCapacity) {
return ByteBuffer.allocateDirect(initialCapacity);
}
就是调用jdk底层去创建directbytebuffer。
回去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();
}
把刚刚调用allocateDirect分配的buffer赋值到this.buffer里面去。
注意memoryAddress,从这PlatformDependent.directBufferAddress(buffer);一直进去:
static long directBufferAddress(ByteBuffer buffer) {
return getLong(buffer, ADDRESS_FIELD_OFFSET);
}
也就是,我们获取到内存地址,加上offset,就是我们能用的开始的地址(通过buffer算出内存地址保存到memoryAddress,这样就可以知道buffer在内存里面的地址是多少了),getLong继续就是:
private static long getLong(Object object, long fieldOffset) {
return UNSAFE.getLong(object, fieldOffset);
}
调用了jdk底层unsafe加上offset为我们获取我们能用的direct地址,最终回来保存到memoryAddress里面。
回到UnpooledUnsafeDirectByteBuf的_getByte方法:
@Override
protected byte _getByte(int index) {
return UnsafeByteBufUtil.getByte(addr(index));
}
看addr(index):
long addr(int index) {
return memoryAddress + index;
}
这个memoryAddress就是上面那个,也就是通过memoryAddress就能操作unsafedirect buffer内存了(通过memoryAddress+index当做内存地址)。
好,这就是UnpooledByteBufAllocator分配和操作direct unsafe内存的分析了
看非unsafe,一样是下面这个代码:
@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);
}
从UnpooledDirectByteBuf进入:
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();
}
可以看到没有memoryAddress了,只有buffer。在UnpooledDirectByteBuf的_getByte方法里面,就是直接读取buffer里面的内容了:
@Override
protected byte _getByte(int index) {
return buffer.get(index);
}
再总结一下,
UnpooledHeapByteBuf和UnpooledUnsafeHeapByteBuf存储的时候,都是通过一个array。但是_getByte(array,index)的时候,UnpooledHeapByteBuf底层是直接操作array;而 UnpooledUnsafeHeapByteBuf是通过UNSAFE操作这个数组。
UnpooledDirectByteBuf和UnpooledUnsafeDirectByteBuf存储的时候,都通过了一个buffer存储(而且UnpooledUnsafeDirectByteBuf还会计算这个buffer在内存的地址memoryAddress,以后通过操作这个memoryAddress+index操作buffer)。_getByte(index)的时候UnpooledDirectByteBuf通过操作这个buffer操作数据;而UnpooledUnsafeDirectByteBuf通过操作unsafe操作index'(这个index'是memoryAddress+index得来的)来操作数据。(但是其实操作jdk底层buffer的时候都是通过unsafe操作的,所以direct的数据最终都是通过unsafe操作的)