Netty學習五:Buffers

1. Netty中的緩衝

在Netty中並無使用Java自帶的ByteBuffer,而是本身實現提供了一個緩存區來用於標識一個字節序列,並幫助用戶操做原始字節或者自定義的POJO。

Java NIO的ByteBuffer問題數組

  • 長度固定,不能動態擴展和收縮
  • 只有一個標識位置的指針,讀寫時要手動調用相關方法
  • API功能有限,須要本身實現更高級的功能

如上圖,channel與對端的I/O讀寫都要操做Buffers。當有讀操做時,把數據從內核區讀取到用戶區,當有寫操做時,把數據從用戶區寫到內核區。

2. ByteBuf

ByteBuf是Netty的實現的最基本的數據緩衝,它包括Heap Buffer和Direct Buffer。

ByteBuf實現了高級的功能和API,是Java NIO ByteBuffer更高級的封裝和實現。

2.1 ByteBuf結構

如上圖是ByteBuf的結構,其中:緩存

  • readerIndex:讀指針,最開始時等於0
  • writerIndex:寫指針,最開始時等於0
  • capacity:表明這塊內存區域的大小

2.2 寫入數據

當寫入N個數據後,writerIndex向後移動,但要保證writerIndex<capacity,這時可讀數據爲writerIndex,可寫數據長度爲capacity-writerIndex

2.3 讀取數據

每讀一個數據,readerIndex向後移動,但readerIndex<=writerIndex,可讀數據writerIndex-readerIndex這時readerIndex-0則是可丟棄的數據段

2.4 動態擴展

若是數據繼續寫入緩衝區,而緩存區的writable區間已經被判斷空間不足,那就有兩個方法能夠解決:
  • 動態擴展緩衝區,由ByteBuf實現線程

    從新建立一個新的ByteBuffer,並將以前的ByteBuffer複製到新建立的ByteBuffer中,最後釋放老的ByteBuffer3d

  • 重用discard 區域,須要調用discardReadBytes方法指針

2.5 Heap Buffer

Heap Buffer:堆內存字節緩存區,特色是內存的分配和回收速度快,但在I/O讀寫時須要額外一次內存複製,將堆內存對應的緩衝區複製到內核Channel中。

2.6 Direct Buffer

Direct Buffer:直接內存字節緩存區,直接從Socket Channel中讀寫數據,少了一次內存複製,但內存的分配和回收速度慢一些。

2.7 Composite Buffer

Composite Buffer:複合緩衝區,咱們能夠建立多個不一樣的ByteBuf,而後提供一個這些ByteBuf 組合的視圖。複合緩衝區就像一個列表,咱們能夠動態的添加和刪除其中的ByteBuf

2.8 引用計數

ByteBuf擴展了ReferenceCountered接口,這個接口定義的功能主要是引用計數。
經過refCnt的數值來計算ByteBuf,若是refCnt等於1了,調用deallocate來回收ByteBuf,對於池化的ByteBuf則放入內存池,其餘則釋放內存。

3. 緩衝對象池

Netty使用Recycler來實現輕量級緩衝對象池。

3.1 Recycler

  • Handlenetty

    定義接口code

  • Stack對象

    Stack具體維護着對象池數據,向Recycler提供push和pop兩個主要訪問接口,pop用於從內部彈出一個可被重複使用的對象,push用於回收之後能夠重複使用的對象blog

  • elements接口

    對象池數組

3.1.1 get

  • 首先得到當前線程關聯的stack
  • 從stack中pop出一個handle
  • 若是沒有則k新建一個
  • 從handle中取出對象

3.1.2 recycle

  • 對象經過引用計數來管理,若是發現引用計數等於1了,就通知Recycler回收對象
  • 將對象放入stack中
  • 若是超過當前池的大小但還沒達到最大值,則新建池,並拷貝對象

3.2 內存池總結

Netty中每一個線程都關聯着一個內存對象池,用來操做緩衝對象

Netty中的I/O操做都須要涉及緩衝區,若是不使用內存池技術,系統將頻繁的建立和回收buffer,對buffer的管理將十分困難。對於HeapBuf和Directbuf都分配和釋放頻率,而且對於Directbuf彌補必定的分配回收代價。

4. Zero-Copy

  • Netty 提供了 CompositeByteBuf 類, 它能夠將多個 ByteBuf 合併爲一個邏輯上的 ByteBuf, 避免了各個 ByteBuf 之間的拷貝

  • 經過 wrap 操做, 咱們能夠將 byte[] 數組、ByteBuf、ByteBuffer等包裝成一個 Netty ByteBuf 對象, 進而避免了拷貝操做

  • ByteBuf 支持 slice 操做, 所以能夠將 ByteBuf 分解爲多個共享同一個存儲區域的 ByteBuf, 避免了內存的拷貝

  • 經過 channel的tranferTo 實現文件傳輸, 能夠直接將文件緩衝區的數據發送到目標 Channel, 避免了傳統經過循環 write 方式致使的內存拷貝問題.

相關文章
相關標籤/搜索