標籤:擴容 orm 完成 protect buffer needed 影響 def released
UnpooledHeapByteBuf 是基於堆記憶體進行記憶體配置的位元組緩衝區,沒有基於對象池技術實現,這意味著每次I/O的讀寫都會建立一個新的UnpooledHeapByteBuf,頻繁進行大塊記憶體的分配和回收對效能會造成一定的影響,但是對比與堆外記憶體的申請和釋放,它的成本會低一些。
相對與PooledHeapByteBuf,UnpooledHeapByteBuf 的實現原理更加簡單,也不容易出現記憶體管理方面的問題,在滿足效能的情況下,盡量使用UnpooledHeapByteBuf 。
1.成員變數
private final ByteBufAllocator alloc;//彙總一個ByteBufAllocator,用於UnpooledHeapByteBuf的記憶體配置private byte[] array;//byte 數組作為緩衝區private ByteBuffer tmpNioBuf;//用於實現Netty ByteBuf 到 JDK ByteBuffer 的轉換
事實上,如果使用JDK 的ByteBuffer替換byte數組也是可行的 ,直接使用byte數組的根本原因是提升效能和更加便捷的進行位操作。JDK 的ByteBuffer底層實現也是byte數組,如下。
public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>{ // These fields are declared here rather than in Heap-X-Buffer in order to // reduce the number of virtual method invocations needed to access these // values, which is especially costly when coding small buffers. // final byte[] hb; // Non-null only for heap buffers final int offset; boolean isReadOnly; // Valid only for heap buffers
2.動態擴充緩衝區
@Overridepublic ByteBuf capacity(int newCapacity) { ensureAccessible(); if (newCapacity < 0 || newCapacity > maxCapacity()) { throw new IllegalArgumentException("newCapacity: " + newCapacity); } int oldCapacity = array.length; if (newCapacity > oldCapacity) { byte[] newArray = new byte[newCapacity]; System.arraycopy(array, 0, newArray, 0, array.length); setArray(newArray); } else if (newCapacity < oldCapacity) { byte[] newArray = new byte[newCapacity]; int readerIndex = readerIndex(); if (readerIndex < newCapacity) { int writerIndex = writerIndex(); if (writerIndex > newCapacity) { writerIndex(writerIndex = newCapacity); } System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex); } else { setIndex(newCapacity, newCapacity); } setArray(newArray); } return this;}
/** * Should be called by every method that tries to access the buffers content to check * if the buffer was released before. */protected final void ensureAccessible() { if (refCnt() == 0) { throw new IllegalReferenceCountException(0); }}
方法 的入口首先對新容量進行合法性校正,如果大於容量上限或者小於0,則拋出IllegalArgumentException異常。
判斷新的容量值是否大於當前的緩衝區容量,如果大於則需要動態擴充,通過 byte[] newArray = new byte[newCapacity];建立新的緩衝區位元組數組,然後通過 System.arraycopy進行記憶體複製,將舊的位元組數組複製到新建立的位元組數組中,最後調用setArray(newArray);替換舊的位元組數組。
private void setArray(byte[] initialArray) { array = initialArray; tmpNioBuf = null;}
動態擴容完成後,需要將原來的視圖tmpNioBuf設定為空白。
如果新的容量小於當前緩衝區容量不需要動態擴充,但是需要截取當前緩衝區建立一個新的子緩衝區。先判斷下讀索引是否小於新的容量值,如果小於進一步判斷寫索引是否大於新的容量值,如果大於則將寫索引設定為新的容量值(防止越界)。更新完寫索引之後,通過 System.arraycopy將當前可讀的位元組數組複製到新建立的子緩衝區中, System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
如果新的容量值小於讀索引,說明沒有可讀的位元組數組需要複製到新建立的緩衝區中,將讀寫索引為新的容量值即可。最後調用setArray方法替換原來的位元組數組。
3.位元組數組複製
@Overridepublic 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;}
首先做合法性校正。
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) { checkIndex(index, length); if (srcIndex < 0 || srcIndex > srcCapacity - length) { throw new IndexOutOfBoundsException(String.format( "srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity)); }}
校正index,length的值,如果小於0,拋出IllegalArgumentException異常,然後對兩者之和進行判斷,如果大於緩衝區的容量,則拋出IndexOutOfBoundsException異常。srcIndex和srcCapacity,與之類似。校正通過之後,調用 System.arraycopy(src, srcIndex, array, index, length)方法進行位元組數組的複製。
protected final void checkIndex(int index, int fieldLength) { ensureAccessible(); if (fieldLength < 0) { throw new IllegalArgumentException("length: " + fieldLength + " (expected: >= 0)"); } if (index < 0 || index > capacity() - fieldLength) { throw new IndexOutOfBoundsException(String.format( "index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity())); }}
注意: ButeBuf以set和get開頭讀寫緩衝區的方法不會修改讀寫索引。
4.轉換為JDK ByteBuffer
ByteBuffer 是基於byte數組實現,NIO的ByteBuffer提供wrap方法,可以將byte數群組轉換成ByteBuffer對象。
/** * Wraps a byte array into a buffer. * * <p> The new buffer will be backed by the given byte array; * that is, modifications to the buffer will cause the array to be modified * and vice versa. The new buffer‘s capacity will be * <tt>array.length</tt>, its position will be <tt>offset</tt>, its limit * will be <tt>offset + length</tt>, and its mark will be undefined. Its * {@link #array backing array} will be the given array, and * its {@link #arrayOffset array offset} will be zero. </p> * * @param array * The array that will back the new buffer * * @param offset * The offset of the subarray to be used; must be non-negative and * no larger than <tt>array.length</tt>. The new buffer‘s position * will be set to this value. * * @param length * The length of the subarray to be used; * must be non-negative and no larger than * <tt>array.length - offset</tt>. * The new buffer‘s limit will be set to <tt>offset + length</tt>. * * @return The new byte buffer * * @throws IndexOutOfBoundsException * If the preconditions on the <tt>offset</tt> and <tt>length</tt> * parameters do not hold */ public static ByteBuffer wrap(byte[] array, int offset, int length) { try { return new HeapByteBuffer(array, offset, length); } catch (IllegalArgumentException x) { throw new IndexOutOfBoundsException(); } }
UnpooledHeapByteBuf.java
@Overridepublic ByteBuffer nioBuffer(int index, int length) { ensureAccessible(); return ByteBuffer.wrap(array, index, length).slice();}
調用了ByteBuffer的slice方法,由於每次調用nioBuffer都會建立一個新的ByteBuffer,因此此處的slice方法起不到重用緩衝區的效果,只能保證讀寫索引的獨立性。
5.與子類相關的方法
isDirect方法:如果基礎堆記憶體實現的ByteBuf,返回false,
@Overridepublic boolean isDirect() { return false;}
hasArray方法:因為UnpooledHeapByteBuf是基於位元組數組實現,返回true
@Overridepublic boolean hasArray() { return true;}
array方法:因為UnpooledHeapByteBuf是基於位元組數組實現,所以傳回值是內部的位元組數群組成員變數。調用array方法之前,可以先使用hasArray方法進行判斷,如果返回false說明當前ByteBuf不支援array方法。
@Overridepublic byte[] array() { ensureAccessible(); return array;}
netty UnpooledHeapByteBuf 源碼分析