摘要:主要用來檢測對象是否泄漏。子類實現相關的方法是否支持數組,判斷緩沖區的實現是否基于字節數組如果緩沖區的實現基于字節數組,返回字節數組
ByteBuf
ByteBuf需要提供JDK ByteBuffer的功能(包含且不限于),主要有以下幾類基本功能:
7種Java基礎類型、byte[]、ByteBuffer(ByteBuf)的等的讀寫
緩沖區自身的copy和slice
設置網絡字節序
構造緩沖區實例
操作位置指針
擴容原理首先確認ByteBuf是否已經被釋放,如果被釋放,則拋出IllegalReferenceCountException異常
判斷寫入需要的最小空間,如果該空間小于ByteBuf的可寫入空間,直接返回,不進行擴容
判斷寫入需要的最小空間,如果該空間大于ByteBuf的(最大容量-當前的寫索引),不進行擴容,拋出IndexOutOfBoundsException異常
計算新容量,動態擴容的規則,當新容量大于4MB時,以4MB的方式遞增擴容,在小于4MB時,從64字節開始倍增(Double)擴容
讀寫索引Netty提供readIndex和writeIndex用來支持讀取和寫入操作,兩個索引將緩沖區劃分為三個區域
0 ~ readIndex:已讀區域(可丟棄區域)
readIndex ~ writeIndex:未讀取區域
writeIndex ~ capacity:待寫入區域
已讀區域(Discardable Bytes)位于已讀區域的內容表明該內容已被Netty處理完成,我們可以重用這塊緩沖區,盡量減少緩沖區的動態擴容(復制,耗時操作)。
調用discardBytes()方法可以清除已讀區域內容,但同時會導致未讀區域的左移,也是將未讀區域的內容復制到原來的已讀區域(耗時),
因此頻繁的調用discardBytes也是不可取的,可以根據實際情況進行調用。
Readable Bytes(可讀空間)存儲的是未被讀取處理的內容,以read或者skip開頭的方法都會從readIndex開始讀取或者跳過指定的數據,同時readIndex會增加讀取或跳過
的字節數長度。如果讀取的字節數長度大于實際可讀取的字節數,拋出IndexOutOfBoundsException異常。
Writable Bytes(可寫入空間)是未被數據填充的緩沖區塊,以write開頭的操作都會從writeIndex開始向緩沖區寫入數據,同時writeIndex會增加寫入的數據的字節數長度。
如果寫入的字節數大于可寫入的字節數,會拋出IndexOutOfBoundsException異常。
Clear操作并不會清除緩沖區的內容,只是將readIndex和writeIndex還原為初始分配值。
Mark和ResetmarkReadIndex
resetReadIndex
markWriteIndex
resetWriteIndex
查找操作indexOf(int fromIndex, int toIndex, byte value):fromIndex<=toIndex時,從頭開始查找首次出現value的位置(查找范圍fromIndex ~ toIndex),當fromIndex > toIndex時,倒著查找首次出現value的位置(查找的范圍toIndex ~ fromIndex - 1),查不到返回-1
bytesBefore(byte value):從ByteBuf的可讀區域中首次定位出現value的位置,沒有找到返回-1。該方法不會修改readIndex和writeIndex
bytesBefore(int length, byte value):從ByteBuf的可讀區域中定位首次出現value的位置,結束索引是readIndex+length。如果length大于可讀字節數,拋出IndexOutOfBoundsException異常
bytesBefore(int index, int length, byte value):從ByteBuf中定位首次出現value的位置,起始索引為index,結束索引為index+length,如果index+length大于當前緩沖區的容量,拋出IndexOutOfBoundsException異常
forEachByte(int index, int length, ByteProcessor processor):從index開始,到index + length結束,與ByteProcessor設置的查找條件進行對比,滿足條件,返回位置索引,否則返回-1
forEachByteDesc(ByteProcessor processor):倒序遍歷ByteBuf的可讀字節數組,與ByteProcessor設置的查找條件進行對比,滿足條件,返回位置索引,否則返回-1
forEachByteDesc(int index, int length, ByteProcessor processor):以index + length - 1開始,直到index結束,倒序遍歷ByteBuf字節數組,與ByteProcessor設置的查找條件進行對比,滿足條件,返回位置索引,否則返回-1
Netty提供了大量的默認的ByteProcessor,來對常用的查找自己進行查找,具體可見ByteProcessor接口。
Derived buffers(派生緩沖區)duplicate():返回當前ByteBuf的復制對象,復制后返回的ByteBuf與操作的ByteBuf共享緩沖區內容,但是維護自己獨立的讀寫索引。當修改復制后的ByteBuf內容后,原ByteBuf的內容也隨之改變,因為雙方持有的是同一個內容的指針引用。
copy():復制一個新的ByteBuf對象,內容和索引都與原ByteBuf獨立,復制操作本身并不修改原ByteBuf的讀寫索引
copy(int index, int length):復制一個新的ByteBuf對象,復制開始的索引為index,復制的長度為length
slice():返回與當前ByteBuf的可讀子緩沖區,范圍是readIndex ~ writeIndex,返回后的ByteBuf與原ByteBuf內容共享,讀寫索引獨立維護,maxCapacity是當前ByteBuf的可讀字節數(換句話說就是這個新返回的緩沖區不能再進行寫入)
slice(int index, int length):返回index開始,length長度的當前ByteBuf的子緩沖區,返回后的ByteBuf與原ByteBuf內容共享,讀寫索引獨立維護,maxCapacity是length(換句話說就是這個新返回的緩沖區不能再進行寫入)
轉換成標準的ByteBufferByteBuffer nioBuffer():將當前ByteBuf可讀的緩沖區轉換成ByteBuffer,兩者共享同一個緩沖區內容引用,對ByteBuffer的讀寫操作并不會修改原ByteBuf的讀寫索引。返回后的ByteBuffer無法感知ByteBuf的動態擴展。
ByteBuffer nioBuffer(int index, int length):從ByteBuf的index位置開始長度為length的緩沖區轉換成ByteBuffer,兩者共享同一個緩沖區內容引用,對ByteBuffer的讀寫操作并不會修改原ByteBuf的讀寫索引。返回后的ByteBuffer無法感知ByteBuf的動態擴展。
隨機讀寫主要通過set和get開頭的方法,這兩個方法可以指定索引位置。
ByteBuf源碼從內存分配的角度來看,ByteBuf主要分為以下兩類:
堆內存(HeapByteBuf)字節緩沖區:內存分配和回收速度快,可以被JVM自動回收;缺點是如果Socket進行I/O讀寫,需要進行一次內存復制,將堆內存對應的緩沖區復制到內核Channel中,性能會有所下降
直接內存(DirectByteBuf)字節緩沖區:堆外內存直接分配,相比于堆內存,分配和回收速度比較慢,但是在Socket Channel中進行讀寫比較快(少一次內存復制)
ByteBuf的最佳時間是在I/O通信線程的讀寫緩沖區使用DirectByteBuf,后端業務消息的編解碼模塊使用HeapByteBuf。
從內存回收的角度進行分類:
基于對象池的ByteBuf:自己維護了一個內存池,可以重復利用ByteBuf對象,提升內存使用率,降低GC頻率
普通的ByteBuf
AbstractByteBufAbstractByteBuf繼承ByteBuf,ByteBuf中的一些公共屬性和方法會在AbstractByteBuf中實現。
主要變量ResourceLeakDetector
索引設置:讀寫索引、重置讀寫索引、最大容量
讀操作讀操作的公共功能由父類實現,差異化由具體的子類實現。
選取readBytes(byte[] dst, int dstIndex, int length)分析:
首先對緩沖區可讀空間進行校驗:如果讀取的長度(length) < 0,會拋出IllegalArgumentException異常;如果可讀的字節數小于需要讀取的長度(length),會拋出IndexOutOfBoundsException異常
校驗通過之后,調用getBytes方法從當前的讀索引開始進行讀取(這一塊就需要由真正的子類來各自實現),復制length個字節到目標byte數組,數組開始的位置是dstIndex
讀取成功后,對讀索引進行遞增,增加的長度為length
寫操作寫操作的公共功能由父類實現,差異化由具體的子類實現。
選取writeBytes(byte[] src, int srcIndex, int length)分析:
首先對緩沖區的可寫空間進行校驗:如果要寫入的長度(length) < 0,會拋出IllegalArgumentException異常;如果要寫入的長度小于緩沖區可寫入的字節數,表明可寫;如果要寫入的長度 > 最大容量 - writeIndex,會拋出IndexOutOfBoundsException;否則進行擴容操作(擴容操作的原理前面已經講過)。
操作索引與索引相關的操作主要涉及設置讀寫索引、mark、和reset等。
選取readerIndex(int readerIndex)進行分析:
首先對索引合法性進行判斷:如果readerIndex小于0或者readerIndex > writeIndex,則拋出IndexOutOfBoundsException異常
校驗通過之后,將讀索引設置為readerIndex
重用緩沖區選取discardReadBytes()進行分析:
如果readIndex等于0,直接返回
如果readIndex和writeIndex不相等,首先調用setBytes(int index, ByteBuf src, int srcIndex, int length)方法進行字節數組的復制,
然后重新設置markReadIndex、markWriteIndex、readIndex和writeIndex
如果readIndex等于writeIndex,調整markReadIndex和markWriteIndex,不進行字節數組復制,設置readIndex=writeIndex=0
skipBytes校驗跳過的字節長度:如果跳過的字節長度小于0,則拋出IllegalArgumentException異常,如果跳過的字節數大于可讀取的字節數,則拋出IndexOutOfBoundsException異常
校驗通過之后,readIndex增加跳過的字節長度
AbstractReferenceCountedByteBuf該類主要是對引用進行計數,類似于JVM內存回收的對象引用計數器,用于跟蹤對象的分配和銷毀,做自動內存回收。
成員變量AtomicIntegerFieldUpdater
volatile int refCnt:用于跟蹤對象的引用次數,使用volatile是為了解決多線程并發訪問的可見性問題。
對象引用計數器每調用retain()方法一次,引用計數器就會加1,但加完之后會對數據進行校驗,具體的校驗內容如下:
如果加1之前的引用次數小于等于0或者原來的引用次數 + 增加的次數 < 原來的引用次數,則需要還原這次引用計數器增加操作,并且拋出IllegalReferenceCountException異常
UnpooledHeapByteBuf是基于堆內存分配的字節緩沖區,每次I/O讀寫都會創建一個新的UnpooledHeapByteBuf。
成員變量ByteBufAllocator alloc:用于UnpooledHeapByteBuf的內存分配
byte[] array:緩沖區數組,此處也可用ByteBuffer,但是用byte數組的原因是提升性能和更加便捷的進行位操作
ByteBuffer tmpNioBuf:用于實現Netty的ByteBuf到JDK NIO ByteBuffer的轉換
動態擴展緩沖區校驗新容量:如果新容量小于0或者新容量大于最大容量,拋出IllegalArgumentException異常,否則校驗通過
如果新容量大于舊容量,使用new byte[newCapacity]創建新的緩沖數組,然后通過System.arraycopy進行復制,將舊的緩沖區內容拷貝到新的緩沖區中,最后在ByteBuf中替換舊的數組,并且將原來的ByteBuffer tmpNioBuf置為空
如果新容量小于舊容量,使用new byte[newCapacity]創建新的緩沖數組,如果讀索引小于新容量(如果寫索引大于新容量,將寫索引直接置為新容量),然后通過System.arraycopy將當前可讀的緩沖區內容復制到新的byte數組,如果讀索引大于新容量,說明沒有可以拷貝的緩沖區,直接將讀寫索引置為新容量,并且使用新的byte數組替換原來的字節數組
字節數組復制setBytes(int index, byte[] src, int srcIndex, int length)
首先是合法性校驗,先是校驗index,length,如果這兩個值有小于0,或者相加小于0,或者兩個相加大于ByteBuf的容量,則拋出IndexOutOfBoundsException異常,接著校驗被復制的數組的長度和索引問題(srcIndex、length),如果srcIndex、length小于0,或者兩個相加小于0,或者兩個相加超過了src字節數組的容量,也拋出IndexOutOfBoundsException異常
校驗通過之后,使用System.arraycopy方法進行字節數組的拷貝
ByteBuf以get和set開頭讀寫緩沖區的方法不會修改讀寫索引
轉換成JDK ByteBuffer由于UnpooledHeapByteBuf緩沖區采用了byte數組實現,同樣的ByteBuffer底層也是用了byte數組實現,同時ByteBuffer還提供了wrap方法,
直接將字節數組轉換成ByteBuffer,最后調用slice方法。由于每次調用都會創建一個新的ByteBuffer,因此起不到重用緩沖區內容的效果。
hasArray():是否支持數組,判斷緩沖區的實現是否基于字節數組
array():如果緩沖區的實現基于字節數組,返回字節數組
PooledByteBuf PoolArenaArena是指一塊區域,在內存管理中,Memory Arena指內存中的一大塊連續的區域,PoolArena是Netty的內存池實現類。
為了集中管理內存的分配和釋放,同時提高分配和釋放內存的性能,框架會預先申請一大塊內存,然后通過提供相應的分配和釋放接口來使用內存。由于不再使用系統調用來申請和釋放內存,
應用或者系統的性能大大提高。預先申請的那一大塊內存稱之為Memory Arena。
Netty的PoolArena是由多個Chunk組成的大塊內存區域,每個Chunk由一個或者多個Page組成。因此,對內存的組織管理主要集中在如何組織管理Chunk和Page。
PoolChunkChunk主要用來組織和管理多個Page的內存分配。Netty中,Chunk中的Page被構造成一棵二叉樹。
每一個Page可以成為一個節點,第一層的Page節點用來分配所有Page需要的內存。每個節點記錄了自己在Memory Arena中的偏移地址,當一個節點代表的內存區域被分配出去以后,
該節點會被標記為已分配,從這個節點往下的所有節點在后面的內存分配請求中都會被忽略。
在內存分配查找節點時,對樹的遍歷采用深度優先的算法,但在選擇在哪個子節點繼續遍歷時則是隨機的,并不總是訪問左邊的子節點。
PoolSubpage對于小于一個Page的內存,Netty在Page中完成分配。每個Page會被切分成大小相等的多個存儲塊,存儲塊的大小由第一次申請的內存塊大小決定。
一個Page只能用于分配與第一次申請時大小相同的內存。
Page中存儲區域的使用狀態通過一個long數組來維護,數組中每個long的每一位表示一個塊存儲區域的占用情況:0表示未占用,1表示已占用。
內存回收策略Chunk和Page都通過狀態位來標識內存是否可用,不同的是Chunk通過在二叉樹上對節點進行標識實現,Page是通過維護塊的使用狀態標識來實現。
PooledDirectByteBufPooledDirectByteBuf基于內存池實現。
創建字節緩沖區實例新創建PooledDirectByteBuf對象不能直接new,而是從內存池Recycler
設置讀寫索引、標記讀寫索引為0。
copy(int index, int length)方法可以復制一個ByteBuf實例,并且與原來的ByteBuf相互獨立。
首先校驗索引和長度的合法性
校驗通過之后,調用PooledByteBufAllocator分配一個新的ByteBuf,最終會調用PooledByteBufAllocator中的newDirectBuffer(int initialCapacity, int maxCapacity)方法進行內存的分配
在newDirectBuffer中,直接從緩存中獲取ByteBuf而不是創建一個新的對象
ByteBuf輔助類 ByteBufHolderByteBufHolder是BytBuf容器。比如,Http協議的請求消息和應答消息都可以攜帶消息體,這個消息體在Netty中就是ByteBuf對象。由于不同的協議消息體可以包含不同的
協議字段和功能,因此需要對ByteBuf進行包裝和抽象,為了滿足這些定制化的需求,Netty抽象出了ByteBufHolder對象。
ByteBufAllocator是字節緩沖區分配器,按照Netty緩沖區的實現不同可以分為:基于內存池的字節緩沖區分配器和普通的字節緩沖區分配器。
方法名稱 | 返回值說明 | 功能說明 |
---|---|---|
buffer() | ByteBuf | 分配一個字節緩沖區,緩沖區的類型由ByteBufAllocator的實現類決定 |
buffer(int initialCapacity) | ByteBuf | 分配一個初始容量為initialCapacity的字節緩沖區,緩沖區的類型由ByteBufAllocator的實現類決定 |
buffer(int initialCapacity, int maxCapacity) | ByteBuf | 分配一個初始容量為initialCapacity,最大容量為maxCapacity的字節緩沖區,緩沖區的類型由ByteBufAllocator的實現類決定 |
ioBuffer(int initialCapacity, int maxCapacity) | ByteBuf | 分配一個初始容量為initialCapacity,最大容量為maxCapacity的Direct Buffer,Direct Buffer I/O性能高 |
heapBuffer(int initialCapacity, int maxCapacity) | ByteBuf | 分配一個初始容量為initialCapacity,最大容量為maxCapacity的Heap Buffer |
directBuffer(int initialCapacity, int maxCapacity) | ByteBuf | 分配一個初始容量為initialCapacity,最大容量為maxCapacity的Direct Buffer |
compositeBuffer(int maxNumComponents) | CompositeByteBuf | 分配一個最多包含maxNumComponents個緩沖區的復合緩沖區,緩沖區的類型由ByteBufAllocator的實現類決定 |
isDirectBufferPooled() | boolean | 是否使用了直接內存池 |
calculateNewCapacity(int minNewCapacity, int maxCapacity) | int | 動態擴容時計算新容量 |
CompositeByteBuf允許將多個ByteBuf的實例組裝到一起。
CompositeByteBuf定義了一個Component類型的集合,Component實際上是ByteBuf的包裝實現類,它聚合了ByteBuf對象,維護ByteBuf在集合中的位置偏移量等信息。
CompositeByteBuf支持動態增加(addComponent(ByteBuf buffer))和刪除(removeComponent(int cIndex))ByteBuf,增加或刪除ByteBuf之后,
需要更新各個ByteBuf的索引偏移量。
ByteBufUtil提供了大量的靜態方法來操作ByteBuf。列舉三個:
ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset):對需要編碼的字符串src按照指定的字符集charset進行編碼,利用指定的ByteBufAllocator生成一個ByteBuf
decodeString(ByteBuf src, int readerIndex, int len, Charset charset):從指定索引readIndex開始往后len個字節長度,對ByteBuf對象src按照指定的字符集charset進行解碼
hexDump(ByteBuf buffer):將ByteBuf對象的參數內容以十六進制的格式輸出
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72361.html
摘要:轉發自 轉發自 http://netty.io/wiki/referenc... Since Netty version 4, the life cycle of certain objects are managed by their reference counts, so that Netty can return them (or their shared resources)...
摘要:根據對的定義即所謂的就是在操作數據時不需要將數據從一個內存區域拷貝到另一個內存區域因為少了一次內存的拷貝因此的效率就得到的提升在層面上的通常指避免在用戶態與內核態之間來回拷貝數據例如提供的系統調用它可以將一段用戶空間內存映射到內 根據 Wiki 對 Zero-copy 的定義: Zero-copy describes computer operations in which the C...
摘要:提供了作為它的字節容器但是這個類使用起來過于復雜而且也有些繁瑣的的代替品是的的數據處理通過兩個組件暴露下面是的優點它可以被用戶自定義的緩沖區類擴展通過內置的復合緩沖區類型實現了透明的零拷貝容量可以按需增長在讀和寫這兩種模式之間雀環不需要調用 Java NIO 提供了 ByteBuffer 作為它的字節容器, 但是這個類使用起來過于復雜, 而且也有些繁瑣. Netty 的 ByteBuf...
摘要:使用來優化套接字操作,盡可能消除由的緩沖區實現所導致的性能以及內存使用率的懲罰,這種優化發生在的核心代碼中,不會被暴露出來。當前將會被增加所寫入的字節數。 ByteBuf是Java NIO ByteBuffer的替代品,是網絡數據基本單位字節的容器。 ByteBuf的API Netty的數據處理API通過兩個組件暴漏:抽象類ByteBuf和接口ByteBufHolder ByteBuf...
摘要:當你從讀取時,它的將會被遞增已經被讀取的字節數。達到和位于同一位置,表示我們到達可以讀取的數據的末尾。該應用程序可以選擇為多個消息重用相同的消息主體。 ByteBuffer 當我們進行數據傳輸的時候,往往需要使用到緩沖區,常用的緩沖區就是JDK NIO類庫提供的java.nio.Buffer。 showImg(https://segmentfault.com/img/bVbbz8p?w...
閱讀 1660·2021-11-12 10:35
閱讀 1611·2021-08-03 14:02
閱讀 2655·2019-08-30 15:55
閱讀 2024·2019-08-30 15:54
閱讀 735·2019-08-30 14:01
閱讀 2421·2019-08-29 17:07
閱讀 2246·2019-08-26 18:37
閱讀 3028·2019-08-26 16:51