Netty ByteBuf
ByteBuf的基本結構
ByteBuf由一段地址空間碱呼,一個read index和一個write index組成。兩個index分別記錄讀寫進度燥滑,省去了NIO中ByteBuffer手動調(diào)用flip和clear的煩惱纺弊。
+-------------------+------------------+------------------+
| discardable bytes | readable bytes | writable bytes |
| | (CONTENT) | |
+-------------------+------------------+------------------+
| | | |
0 <= readerIndex <= writerIndex <= capacity
通過上圖可以很好的理解ByteBuf的數(shù)據(jù)劃分筛璧。writer index到capacity之間的部分是空閑區(qū)域,可以寫入數(shù)據(jù)惹恃;reader index到writer index之間是已經(jīng)寫過還未讀取的可讀數(shù)據(jù)夭谤;0到reader index是已讀過可以釋放的區(qū)域。
三個index之間的關系是:reader index <= writer index <= capacity
存儲空間
ByteBuf根據(jù)其數(shù)據(jù)存儲空間不同有可以分為三種:基于JVM堆內(nèi)的巫糙,基于直接內(nèi)存的和組合的朗儒。
堆內(nèi)受JVM垃圾收集器的管轄,使用上相對安全一些参淹,不用每次手動釋放醉锄。弊端是GC是會影響性能的;還有就是內(nèi)存的拷貝帶來的性能損耗(JVM進程到Socket)浙值。
直接內(nèi)存則不受JVM的管轄恳不,省去了向JVM拷貝數(shù)據(jù)的麻煩。但是壞處就是別忘了釋放內(nèi)存开呐,否則就會發(fā)生內(nèi)存泄露烟勋。相比于堆內(nèi)存,直接內(nèi)存的的分配速度也比較慢筐付。
最佳實踐:在IO通信的線程中的讀寫B(tài)uffer使用DirectBuffer(省去內(nèi)存拷貝的成本)卵惦,在后端業(yè)務消息的處理使用HeapBuffer(不用擔心內(nèi)存泄露)。
通過hasArray檢查一個ByteBuf heap based還是direct buffer瓦戚。
創(chuàng)建ByteBuf
ByteBuf提供了兩個工具類來創(chuàng)建ByteBuf沮尿,分別是支持池化的Pooled和普通的Unpooled。Pooled緩存了ByteBuf的實例较解,提高性能并且減少內(nèi)存碎片蛹找。它使用Jemalloc來高效的分配內(nèi)存。
如果在Channel中我們可以通過channel.alloc()來拿到ByteBufAllocator哨坪,具體它使用Pool還是Unpool,Directed還是Heap取決于程序的配置乍楚。
索引的標記與恢復
markReaderIndex和resetReaderIndex是一個成對的操作当编。markReaderIndex可以打一個標記,調(diào)用resetReaderIndex可以把readerIndex重置到原來打標記的位置徒溪。
空間釋放
discardReadByte可以把讀過的空間釋放忿偷,這時buffer的readerIndex置為0,可寫空間和writerIndex也會相應的改變臊泌。discardReadBytes在內(nèi)存緊張的時候使用用鲤桥,但是調(diào)用該方法會伴隨buffer的內(nèi)存整理的。這是一個expensive的操作渠概。
clear是把readerIndex和writerIndex重置到0茶凳。但是嫂拴,它不會進行內(nèi)存整理,新寫入的內(nèi)容會覆蓋掉原有的內(nèi)容贮喧。
ByteBuf的派生與復制
派生操作會產(chǎn)生一個新的ByteBuf實例筒狠。這里的新指得是ByteBuf的引用是新的所有的index也是新的。但是它們共用著一套底層存儲箱沦。派生函數(shù):
- duplicate()
- slice()
- slice(int, int)
- readSlice(int)
- retainedDuplicate()
- retainedSlice()
- retainedSlice(int, int)
- readRetainedSlice(int)
如果想要復制一個全新的ByteBuffer請使用copy辩恼,這會完全的復制一個新的ByteBuf出來。
引用計數(shù)
引用計數(shù)記錄了當前ByteBuf被引用的次數(shù)谓形。新建一個ByteBuf它的refCnt是1灶伊,當refCnt == 0時,這個ByteBuf即可被回收寒跳。
引用技術主要用于內(nèi)存泄露的判斷聘萨,Netty提供了內(nèi)存泄露檢測工具。通過使用參數(shù)-Dio.netty.leakDetectionLevel=${level}
可以配置檢測級別:
- 禁用(DISABLED: 完全禁止泄露檢測冯袍,省點消耗匈挖。
- 簡單(SIMPLE): 默認等級,告訴我們?nèi)拥?%的ByteBuf是否發(fā)生了泄露康愤,但總共一次只打印一次儡循,看不到就沒有了。
- 高級(ADVANCED): 告訴我們?nèi)拥?%的ByteBuf發(fā)生泄露的地方征冷。每種類型的泄漏(創(chuàng)建的地方與訪問路徑一致)只打印一次择膝。對性能有影響。
- 偏執(zhí)(PARANOID): 跟高級選項類似检激,但此選項檢測所有ByteBuf肴捉,而不僅僅是取樣的那1%。對性能有絕大的影響叔收。
查詢
很多時候需要從ByteBuf中查找特定的字符齿穗,比如LineBasedFrameDecoder需要在ByteBuf中查找'\r\n'。ByteBuf提供了簡單的indexOf這樣的函數(shù)饺律。同時也可以使用ByteProcesser來查找窃页。
以下gist提供了一些example。