1 堆内内存的分配
现在来看UnpooledByteBufAllocator的内存分配原理。首先是heapBuffer的分配逻辑,newHeapBuffer方法的代码如下:
@Override
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
: new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}
通过调用PlatformDependent.hasUnsafe()方法来判断操作系统是否支持Unsafe,如果支持Unsafe则创建UnpooledUnsafeHeapByteBuf类,否则创建UnpooledHeapByteBuf类。先看一下UnpooledUnsafeHeapByteBuf的构造器会进行哪些操作,代码如下:
final class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf {
/**
* Creates a new heap buffer with a newly allocated byte array.
*
* @param initialCapacity the initial capacity of the underlying byte array
* @param maxCapacity the max capacity of the underlying byte array
*/
UnpooledUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(alloc, initialCapacity, maxCapacity);
}
}
发现UnpooledUnsafeHeapByteBuf继承了UnpooledHeapByte,并且在UnpooledUnsafeHeapByteBuf的构造器中直接调用了super方法,也就是其父类UnpooledHeapByte的构造方法。看一下UnpooledHeapByte的构造方法。
public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
private final ByteBufAllocator alloc;
byte[] array;
private ByteBuffer tmpNioBuf;
/**
* Creates a new heap buffer with a newly allocated byte array.
*
* @param initialCapacity the initial capacity of the underlying byte array
* @param maxCapacity the max capacity of the underlying byte array
*/
protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
}
protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) {
this(alloc, initialArray, 0, initialArray.length, maxCapacity);
}
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);
}
}
其中调用了一个关键方法就是setArray()方法。这个方法的功能非常简单,就是把默认分配的数组new byte[initialCapacity]赋值给全局变量initialArray数组。
private void setArray(byte[] initialArray) {
array = initialArray;
tmpNioBuf = null;
}
紧接着调用setIndex方法。
@Override
public ByteBuf setIndex(int readerIndex, int writerIndex) {
if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
throw new IndexOutOfBoundsException(String.format(
"readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
readerIndex, writerIndex, capacity()));
}
setIndex0(readerIndex, writerIndex);
return this;
}
final void setIndex0(int readerIndex, int writerIndex) {
this.readerIndex = readerIndex;
this.writerIndex = writerIndex;
}
最终setIndex0()方法中初始化readerIndex属性和writerIndex属性。
既然UnpooledUnsafeHeapByteBuf和UnpooledHeapByteBuf调用的都是UnpooledHeapByteBuf的构造方法,那么他们之间到底有什么区别呢?根本区别在于I/O的读写,分别来看它们的getByte()方法,了解二者的区别。
先看UnpooledHeapByteBuf的getByte()方法的实现代码。
@Override
public byte getByte(int index) {
ensureAccessible();
return _getByte(index);
}
@Override
protected byte _getByte(int index) {
return HeapByteBufUtil.getByte(array, index);
}
可以看到,最终调用的是HeapByteBufUtil.getByte(array, index)方法。
static byte getByte(byte[] memory, int index) {
return memory[index];
}
getByte()这个方法中的处理逻辑非常简单,就是根据index索引直接从数组中取值。接着来看UnpooledUnsafeHeapByteBuf的getByte()方法。
@Override
public byte getByte(int index) {
checkIndex(index);
return _getByte(index);
}
@Override
protected byte _getByte(int index) {
return UnsafeByteBufUtil.getByte(array, index);
}
static byte getByte(byte[] array, int index) {
return PlatformDependent.getByte(array, index);
}
可以看到,最终调用的是UnsafeByteBufUtil.getByte(array, index)的方法。通过这样对比代码,已经基本了解UnpooledUnsafeHeapByteBuf和UnpooledHeapByteBuf的区别了。
2 堆外内存的分配
再回到UnpooledHeapByteBuf的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);
}
这段代码可以看出,如果支持Unsafe则调用UnsafeByteBufUtil.newUnsafeDirectByteBuf,否则创建UnpooledDirectByteBuf对象。看一下UnpooledDirectByteBuf构造器。
public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
private final ByteBufAllocator alloc;
private ByteBuffer buffer;
private ByteBuffer tmpNioBuf;
private int capacity;
private boolean doNotFree;
/**
* 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 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));
}
}
首先调用ByteBuffer.allocateDirect.allocatoDirect()通过JDK底层分配了一个直接缓冲区,然后传给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();
}
上面代码可以看到,setByteBuffer方法主要做了一次赋值。继续看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);
}
这个方法返回了一个UnpooledUnsafeDirectByteBuf对象。下面继续来看UnpooledUnsafeDirectByteBuf构造器中的代码。
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);
}
UnpooledUnsafeDirectByteBuf构造器的逻辑和UnpooledDirectByteBuf构造器的逻辑是相似的,其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();
}
同样还是先保存在JDK底层创建的Buffer,接下来有个很重要的操作就是调用PlatformDependent.directBufferAddress(buffer)方法获取Buffer真是的内存地址,并保存到memoryAddress变量中。PlatformDependent.directBufferAddress(buffer)的实现代码如下:
public static long directBufferAddress(ByteBuffer buffer) {
return PlatformDependent0.directBufferAddress(buffer);
}
static long directBufferAddress(ByteBuffer buffer) {
return getLong(buffer, ADDRESS_FIELD_OFFSET);
}
private static long getLong(Object object, long fieldOffset) {
return UNSAFE.getLong(object, fieldOffset);
}
上述代码最终调用了Unsafe的getLong方法,这是一个native方法。直接通过Buffer的内存地址加上一个偏移量去获取数据。到这里,已经基本清楚了UnpooledUnsafeDirectByteBuf和UnpooledDirectByteBuf的区别,非Unsafe通过数组的下标取数据,Unsafe直接操作内存地址,相对于非Unsafe来说,效率当然更高。