ByteBuf是Netty中主要的資料容器與操作工具,也是Netty記憶體管理優化的具體實作,本章我們先從整體上對ByteBuf進行一個概述;
AbstractByteBuf是整個ByteBuf的框架類,定義了各種重要的標志位與API供具體的實作類使用與實作;下面我們就從AbstractByteBuf類入手對ByteBuf的讀寫機制與API進行一個簡單的介紹
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class); private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible"; private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible"; static final boolean checkAccessible; // accessed from CompositeByteBuf private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds"; private static final boolean checkBounds; static { if (SystemPropertyUtil.contains(PROP_CHECK_ACCESSIBLE)) { checkAccessible = SystemPropertyUtil.getBoolean(PROP_CHECK_ACCESSIBLE, true); } else { checkAccessible = SystemPropertyUtil.getBoolean(LEGACY_PROP_CHECK_ACCESSIBLE, true); } checkBounds = SystemPropertyUtil.getBoolean(PROP_CHECK_BOUNDS, true); if (logger.isDebugEnabled()) { logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible); logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds); } } static final ResourceLeakDetector<ByteBuf> leakDetector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class); int readerIndex; int writerIndex; private int markedReaderIndex; private int markedWriterIndex; private int maxCapacity;
在上面的代碼中,我們需要知道ByteBu維護了兩個不同的索引readerIndex與writerIndex,這兩個索引默認都是從0開始,一個用于讀取,一個用于寫入,
當你從ByteBuf讀取資料時,readerIndex會遞增已經讀取的位元組數,同理當你寫入資料時,writerIndex也會隨之遞增,可以說ByteBuf中各種讀寫API都是基于readerIndex與writerIndex來控制的,從索引操作上,ByteBuf中API基本分為兩大部分,會引發索引值遞增的read(讀)和write(寫操作),反之不會引發索引值遞增的get或set操作,下面我們看下ByteBu中常用API的參考說明
ByteBuf API
read操作
| readBoolean() | 回傳當前readIndex的Boolean值,readIndex增加1 |
| readByte(int) | 回傳當前readIndex處的位元組值,readIndex增加1 |
| readUnsignedByte() | 回傳當前readIndex處的無符號位元組值,readIndex增加1 |
| readInt() | 回傳當前readIndex處的int值,readIndex增加4 |
| readUnsignedInt() | 回傳當前readIndex處的無符號int值,回傳型別為long,readIndex增加4 |
| readLong() | 回傳當前readIndex處的long值,readIndex增加8 |
| readShort() |
回傳當前readIndex處的short值,readIndex增加2 |
| readUnsignedShort() | 回傳當前readIndex處的short值,readIndex增加2 |
wirte操作
| writeBoolean() | 在當前writerIndex處寫入一個Boolean值(1或0),writerIndex增加1 |
| writeByte(int) | 在當前writerIndex處寫入一個byte值,writerIndex增加1 |
| writeShort(int) | 在當前writerIndex處寫入一個short值,writerIndex增加2 |
| writeInt(int) | 在當前writerIndex處寫入一個int值,writerIndex增加4 |
| writeLong(int) | 在當前writerIndex處寫入一個long值,writerIndex增加4 |
get操作
| getBoolean(int) | 回傳給的索引處的Boolean值,readIndex值不變 |
| getByte(int) | 回傳給的索引處的Byte值,readIndex值不變 |
| getShort(int) | 回傳給的索引處的short值,readIndex值不變 |
| getInt(int) | 回傳給的索引處的int值,readIndex值不變 |
| getLong(int) | 回傳給的索引處的long值,readIndex值不變 |
set操作
| setBoolean(int,boolean) | 在指定索引處設定一個Boolean值(1或0),writerIndex值不變 |
| setByte(int,int) | 在指定索引處設定一個byte值,writerIndex值不變 |
| setShort(int,int) | 在指定索引處設定一個short值,writerIndex值不變 |
| setInt(int,int) | 在指定索引處設定一個int值,writerIndex值不變 |
| setLong(int,int) | 在指定索引處設定一個long值,writerIndex值不變 |
ByteBuf型別
Netty中ByteBuf相關實作類的UML圖

Netty在構建ByteBuf時,有以下多種分類:
1、從記憶體型別上,分為堆記憶體與直接記憶體,HeapByteBuf與DirectByteBuf
我們知道Java中大部分物件都是在堆記憶體中存盤,由jvm統一管理,但NIO中 的 ByteBuffer 類允許 JVM 實作通過本地呼叫來分配記憶體,Netty中對基于直接記憶體的ByteBuffer也進行了封裝,這主要是為了避免在每次呼叫本地 I/O 操作之前(或者之后)將緩沖區的內容復 制到一個中間緩沖區(或者從中間緩沖區把內容復制到緩沖區),直接緩沖區不會占用堆的容量,事實上,在通過套接字發送它之前,JVM將會在內部把你的緩沖 區復制到一個直接緩沖區中,所以如果使用直接緩沖區可以節約一次拷貝,提高IO操作性能,使用直接記憶體的優缺點如下:
(1)優點:由于資料直接在記憶體中,不存在從JVM拷貝資料到直接緩沖區的程序,提高IO操作性能,
(2)缺點:相對于基于堆的緩沖區,它們的分配和釋放都較為昂貴,同時由于直接記憶體不受JVM垃圾回收統一管理,需要自己手動回收,需要特別注意記憶體泄露的問題,
2、從分配模式上,分為池化與非池化;PooledByteBuf與UnpooledByteBuf
對于頻繁的申請與釋放記憶體帶來的性能損耗與碎片化問題,Netty基于池化思想通過預先申請一塊專用記憶體地址作為記憶體池進行管理,從而不需要每次都進行分配和釋放,
從上面的UML圖中可以看到,基于AbstractByteBuf父類針對直接記憶體與堆記憶體,也都有其對應的池化與非池化實作類;
PooledByteBuf 下有 PooledUnsafeDirectByteBuf、PooledHeapByteBuf、PooledDirectByteBuf三個子類實作
UnpooledByteBuf 下則是 UnpooledDirectByteBuf及其子類UnpooledUnsafeDirectByteBuf 與 UnpooledHeapByteBuf及其子類UnpooledUnsafeHeapByteBuf
3、從具體的操作類上,分為Unsafe與非Unsafe
從上面的子類實作中,我們發現每種分類中又包含Usafe與非Unsafe的區別,我們知道java可以通過Unsafe類直接操作記憶體區域,所以這些類的區別就是在于是呼叫jdk的Unsafe直接去操作物件的記憶體地址還是通過jdk封裝的安全方式操作記憶體,
我們通過PooledByteBuf下Unsafe與非Unsafe實作類的getByte方法,看下具體的區別
PooledUnsafeHeapByteBuf
@Override protected byte _getByte(int index) { return UnsafeByteBufUtil.getByte(addr(index)); }
Netty中封裝了一個UnsafeByteBufUtil類,進入內部實作看到呼叫的是Unsafe物件進行具體操作
static byte getByte(long address) { return UNSAFE.getByte(address); }
PooledHeapByteBuf
跟蹤其內部代碼則可以看到位元組是基于陣列操作的
@Override protected byte _getByte(int index) { return HeapByteBufUtil.getByte(memory, idx(index)); } static byte getByte(byte[] memory, int index) { return memory[index]; }
PooledDirectByteBuf
針對DirectByteBuf大家需要靈活理解,PooledUnsafeDirectByteBuf 與PooledUnsafeDirectByteBuf的區別一個是UnsafeByteBufUtil類直接操作,一個是使用java NIO中的DirectByteBuf類進行操作,但因為要操作直接記憶體,最后還都是要基于jdk的unsafe類實作;
對比他們兩種實作,可以看出Unsafe與非Unsafe的區別,前者是通過jdk的Unsafe類去操作資料,后者直接通過陣列或者jdk底層的DirectByteBuf去操作資料,
ByteBuf作為Netty中資料操作與記憶體分配的具體實作,是Netty中最為底層的也是最精細的一部分,本文只是對ByteBuf的一個簡單概述,后續我們會進一步對其進行探索與剖析,更好的展示Netty內部具體實作,希望對大家能有所幫助,其中如有不足與不正確的地方還望指出與海涵,
關注微信公眾號,查看更多技術文章,

轉載說明:未經授權不得轉載,授權后務必注明來源(注明:來源于公眾號:架構空間, 作者:大凡)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/33612.html
標籤:Java
