日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Netty ByteBuf(图解之 2)| 秒懂

發(fā)布時(shí)間:2024/9/21 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty ByteBuf(图解之 2)| 秒懂 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • Netty ByteBuf(圖解二):API 圖解
    • 源碼工程
    • 寫(xiě)在前面
    • ByteBuf 的四個(gè)邏輯部分
    • ByteBuf 的三個(gè)指針
    • ByteBuf 的三組方法
    • ByteBuf 的引用計(jì)數(shù)
    • ByteBuf 的淺層復(fù)制
    • 寫(xiě)在最后
    • 瘋狂創(chuàng)客圈 Java 死磕系列

Netty ByteBuf(圖解二):API 圖解

瘋狂創(chuàng)客圈 Java 分布式聊天室【 億級(jí)流量】實(shí)戰(zhàn)系列之16 【 博客園 總?cè)肟?/font> 】


源碼工程

源碼IDEA工程獲取鏈接:Java 聊天室 實(shí)戰(zhàn) 源碼

寫(xiě)在前面

? 大家好,我是作者尼恩。

? 今天是百萬(wàn)級(jí)流量 Netty 聊天器 打造的系列文章的第16篇,這是一個(gè)基礎(chǔ)篇,介紹ByteBuf 的使用。

? 由于關(guān)于ByteBuf的內(nèi)容比較多,分兩篇文章:

? 第一篇:圖解 ByteBuf的分配、釋放和如何避免內(nèi)存泄露

? 第二篇:圖解 ByteBuf的具體使用

? 本篇為第二篇

ByteBuf 的四個(gè)邏輯部分

ByteBuf 是一個(gè)字節(jié)容器,內(nèi)部是一個(gè)字節(jié)數(shù)組。

從邏輯上來(lái)分,字節(jié)容器內(nèi)部,可以分為四個(gè)部分:

第一個(gè)部分是已經(jīng)丟棄的字節(jié),這部分?jǐn)?shù)據(jù)是無(wú)效的;

第二部分是可讀字節(jié),這部分?jǐn)?shù)據(jù)是 ByteBuf 的主體數(shù)據(jù), 從 ByteBuf 里面讀取的數(shù)據(jù)都來(lái)自這一部分;

第三部分的數(shù)據(jù)是可寫(xiě)字節(jié),所有寫(xiě)到 ByteBuf 的數(shù)據(jù)都會(huì)寫(xiě)到這一段。

第四部分的字節(jié),表示的是該 ByteBuf 最多還能擴(kuò)容的大小。

四個(gè)部分的邏輯功能,如下圖所示

ByteBuf 的三個(gè)指針

ByteBuf 通過(guò)三個(gè)整型的指針(index),有效地區(qū)分可讀數(shù)據(jù)和可寫(xiě)數(shù)據(jù),使得讀寫(xiě)之間相互沒(méi)有沖突。

這個(gè)三個(gè)指針,分別是:

  • readerIndex(讀指針)
  • writerIndex(寫(xiě)指針)
  • maxCapacity(最大容量)

? 這三個(gè)指針,是三個(gè)int 型的成員屬性,定義在 AbstractByteBuf 抽象基類中。

? 三個(gè)指針的代碼截圖,如下:

readerIndex 讀指針

指示讀取的起始位置。

每讀取一個(gè)字節(jié),readerIndex 自增1 。一旦 readerIndex 與 writerIndex 相等,ByteBuf 不可讀 。

writerIndex 寫(xiě)指針

指示寫(xiě)入的起始位置。

每寫(xiě)一個(gè)字節(jié),writerIndex 自增1。一旦增加到 writerIndex 與 capacity() 容量相等,表示 ByteBuf 已經(jīng)不可寫(xiě)了 。

capacity()容量不是一個(gè)成員屬性,是一個(gè)成員方法。表示 ByteBuf 內(nèi)部的總?cè)萘俊?注意,這個(gè)不是最大容量。

maxCapacity 最大容量

指示可以 ByteBuf 擴(kuò)容的最大容量。

當(dāng)向 ByteBuf 寫(xiě)數(shù)據(jù)的時(shí)候,如果容量不足,可以進(jìn)行擴(kuò)容。

擴(kuò)容的最大限度,直到 capacity() 擴(kuò)容到 maxCapacity為止,超過(guò) maxCapacity 就會(huì)報(bào)錯(cuò)。

capacity()擴(kuò)容的操作,是底層自動(dòng)進(jìn)行的。

ByteBuf 的三組方法

從三個(gè)維度三大系列,介紹ByteBuf 的常用 API 方法。

第一組:容量系列

  • 方法 一:capacity()

    表示 ByteBuf 的容量,包括丟棄的字節(jié)數(shù)、可讀字節(jié)數(shù)、可寫(xiě)字節(jié)數(shù)。

  • 方法二:maxCapacity()

    表示 ByteBuf 底層最大能夠占用的最大字節(jié)數(shù)。當(dāng)向 ByteBuf 中寫(xiě)數(shù)據(jù)的時(shí)候,如果發(fā)現(xiàn)容量不足,則進(jìn)行擴(kuò)容,直到擴(kuò)容到 maxCapacity。

第二組:寫(xiě)入系列

  • 方法一:isWritable()

    表示 ByteBuf 是否可寫(xiě)。如果 capacity() 容量大于 writerIndex 指針的位置 ,則表示可寫(xiě)。否則為不可寫(xiě)。

    isWritable()的源碼,也是很簡(jiǎn)單的。具體如下:

public boolean isWritable() {return this.capacity() > this.writerIndex;}

注意:如果 isWritable() 返回 false,并不代表不能往 ByteBuf 中寫(xiě)數(shù)據(jù)了。 如果Netty發(fā)現(xiàn)往 ByteBuf 中寫(xiě)數(shù)據(jù)寫(xiě)不進(jìn)去的話,會(huì)自動(dòng)擴(kuò)容 ByteBuf。

  • 方法二:writableBytes()

    返回表示 ByteBuf 當(dāng)前可寫(xiě)入的字節(jié)數(shù),它的值等于 capacity()- writerIndex。

    如下圖所示:

  • 方法三:maxWritableBytes()

    返回可寫(xiě)的最大字節(jié)數(shù),它的值等于 maxCapacity-writerIndex 。

  • 方法四:writeBytes(byte[] src)

    把字節(jié)數(shù)組 src 里面的數(shù)據(jù)全部寫(xiě)到 ByteBuf。

    這個(gè)是最為常用的一個(gè)方法。

  • 方法五:writeTYPE(TYPE value) 基礎(chǔ)類型寫(xiě)入方法

    基礎(chǔ)數(shù)據(jù)類型的寫(xiě)入,包含了 8大基礎(chǔ)類型的寫(xiě)入。

    具體如下:writeByte()、 writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble() ,向 ByteBuf寫(xiě)入基礎(chǔ)類型的數(shù)據(jù)。

  • 方法六:setTYPE(TYPE value)基礎(chǔ)類型寫(xiě)入,不改變指針值

    基礎(chǔ)數(shù)據(jù)類型的寫(xiě)入,包含了 8大基礎(chǔ)類型的寫(xiě)入。

    具體如下:setByte()、 setBoolean()、setChar()、setShort()、setInt()、setLong()、setFloat()、setDouble() ,向 ByteBuf 寫(xiě)入基礎(chǔ)類型的數(shù)據(jù)。

    setType 系列與writeTYPE系列的不同

    setType 系列 不會(huì) 改變寫(xiě)指針 writerIndex ;

    writeTYPE系列 會(huì) 改變寫(xiě)指針 writerIndex 的值。

  • 方法七:markWriterIndex() 與 resetWriterIndex()

    這里兩個(gè)方法一起介紹。

    前一個(gè)方法,表示把當(dāng)前的寫(xiě)指針writerIndex 保存在 markedWriterIndex 屬性中;

    后一個(gè)方法,表示把當(dāng)前的寫(xiě)指針 writerIndex 恢復(fù)到之前保存的 markedWriterIndex 值 。

    標(biāo)記 markedWriterIndex 屬性, 定義在 AbstractByteBuf 抽象基類中。

    截圖如下:

第三組:讀取系列

  • 方法一:isReadable()

    表示 ByteBuf 是否可讀。如果 writerIndex 指針的值大于 readerIndex 指針的值 ,則表示可讀。否則為不可寫(xiě)。

    isReadable()的源碼,也是很簡(jiǎn)單的。具體如下:

public boolean isReadable() {return this.writerIndex > this.readerIndex;}
  • 方法二:readableBytes()

    返回表示 ByteBuf 當(dāng)前可讀取的字節(jié)數(shù),它的值等于 writerIndex - readerIndex 。

    如下圖所示:

  • 方法三: readBytes(byte[] dst)

    把 ByteBuf 里面的數(shù)據(jù)全部讀取到 dst 字節(jié)數(shù)組中,這里 dst 字節(jié)數(shù)組的大小通常等于 readableBytes() 。 這個(gè)方法,也是最為常用的一個(gè)方法。

  • 方法四:readType() 基礎(chǔ)類型讀取

    基礎(chǔ)數(shù)據(jù)類型的讀取,可以讀取 8大基礎(chǔ)類型。

    具體如下:readByte()、readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble() ,從 ByteBuf讀取對(duì)應(yīng)的基礎(chǔ)類型的數(shù)據(jù)。

  • 方法五:getTYPE(TYPE value)基礎(chǔ)類型讀取,不改變指針值

    基礎(chǔ)數(shù)據(jù)類型的讀取,可以讀取 8大基礎(chǔ)類型。

    具體如下:getByte()、 getBoolean()、getChar()、getShort()、getInt()、getLong()、getFloat()、getDouble() ,從 ByteBuf讀取對(duì)應(yīng)的基礎(chǔ)類型的數(shù)據(jù)。

    getType 系列與readTYPE系列的不同

    getType 系列 不會(huì) 改變讀指針 readerIndex ;

    readTYPE系列 會(huì) 改變讀指針 readerIndex 的值。

  • 方法六:markReaderIndex() 與 resetReaderIndex()

    這里兩個(gè)方法一起介紹。

    前一個(gè)方法,表示把當(dāng)前的讀指針ReaderIndex 保存在 markedReaderIndex 屬性中。

    后一個(gè)方法,表示把當(dāng)前的讀指針 ReaderIndex 恢復(fù)到之前保存的 markedReaderIndex 值 。

    標(biāo)記 markedReaderIndex 屬性, 定義在 AbstractByteBuf 抽象基類中。

    截圖如下:

ByteBuf 的引用計(jì)數(shù)

Netty 的 ByteBuf 的內(nèi)存回收工作,是通過(guò)引用計(jì)數(shù)的方式管理的。

大致的引用計(jì)數(shù)的規(guī)則如下:

  • 默認(rèn)情況下,當(dāng)創(chuàng)建完一個(gè) ByteBuf 時(shí),它的引用為1。
  • 每次調(diào)用 retain()方法, 它的引用就加 1 ;
  • 每次調(diào)用 release() 方法,是將引用計(jì)數(shù)減 1。

如果引用為0,再次訪問(wèn)這個(gè) ByteBuf 對(duì)象,將會(huì)拋出異常。

如果引用為0,表示這個(gè) ByteBuf 沒(méi)有地方被引用到,需要回收內(nèi)存。

Netty的內(nèi)存回收分為兩種情況:

  • Pooled 池化的內(nèi)存,放入可以重新分配的 ByteBuf 池子,等待下一次分配。
  • Unpooled 未池化的 ByteBuf 內(nèi)存,確保GC 可達(dá),確保 能被 JVM 的 GC 回收器回收到。

ByteBuf 的淺層復(fù)制

ByteBuf 的淺層復(fù)制分為兩種,有切片slice 淺層復(fù)制,和duplicate 淺層復(fù)制。

slice 切片淺層復(fù)制

? 首先說(shuō)明一下,這是一種非常重要的操作。可以很大程度的避免內(nèi)存拷貝。這一點(diǎn),對(duì)于大規(guī)模消息通訊來(lái)說(shuō),是非常重要的。

? slice 操作可以獲取到一個(gè) ByteBuf 的一個(gè)切片。一個(gè)ByteBuf,可以進(jìn)行多次的切片操作,多個(gè)切片可以共享一個(gè)存儲(chǔ)區(qū)域的 ByteBuf 對(duì)象。

? slice 操作方法有兩個(gè)重載版本:

  • public ByteBuf slice();
  • public ByteBuf slice(int index, int length);

    兩個(gè)版本有非常緊密的聯(lián)系。
    不帶參數(shù)的 slice 方法,等同于 buf.slice(buf.readerIndex(), buf.readableBytes()) 調(diào)用, 即返回 ByteBuf 實(shí)例中可讀部分的切片。
    而帶參數(shù) slice(int index, int length) 方法,可以通過(guò)靈活的設(shè)置不同的參數(shù),來(lái)獲取到 buf 的不同區(qū)域的切片。

調(diào)用slice()方法后,返回的 ByteBuf 的切片,大致如下圖:

調(diào)用slice()方法后,返回的ByteBuf 切片的屬性,大致如下:

  • slice 的 readerIndex(讀指針)的值為 0

  • slice 的 writerIndex(寫(xiě)指針) 的 值為源Bytebuf的 readableBytes() 可讀字節(jié)數(shù)。

  • slice 的 maxCapacity(最大容量) 的值為源Bytebuf的 readableBytes() 可讀字節(jié)數(shù)。maxCapacity 與 writerIndex 值相同,切片不可以寫(xiě)。

  • 切片的可讀字節(jié)數(shù),為自己的 writerIndex - readerIndex。所有,切片和源Bytebuf的 readableBytes() 可讀字節(jié)數(shù)相同。

  • 也就是說(shuō),切片可讀,不可寫(xiě)。

slice()切片和原ByteBuf的聯(lián)系

  • 切片不會(huì)拷貝原ByteBuf底層數(shù)據(jù),底層數(shù)組和原ByteBuf的底層數(shù)組是同一個(gè)
  • 切片不會(huì)改變?cè)?ByteBuf 的引用計(jì)數(shù)。

根本上,調(diào)用slice()方法生成的切片,是 源Bytebuf 可讀部分的淺層復(fù)制

下面的例子展示了 ByteBuf.slice 方法的演示:

public static void testSlice() {ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(9, 100);print("allocate ByteBuf(9, 100)", buffer);buffer.writeBytes(new byte[]{1, 2, 3, 4});print("writeBytes(1,2,3,4)", buffer);ByteBuf buffer1= buffer.slice();print("buffer slice", buffer1); }

結(jié)果如下:

after ===========allocate ByteBuf(9, 100)============ capacity(): 9 maxCapacity(): 100 readerIndex(): 0 readableBytes(): 0 isReadable(): false writerIndex(): 0 writableBytes(): 9 isWritable(): true maxWritableBytes(): 100after ===========writeBytes(1,2,3,4)============ capacity(): 9 maxCapacity(): 100 readerIndex(): 0 readableBytes(): 4 isReadable(): true writerIndex(): 4 writableBytes(): 5 isWritable(): true maxWritableBytes(): 96after ===========buffer slice============ capacity(): 4 maxCapacity(): 4 readerIndex(): 0 readableBytes(): 4 isReadable(): true writerIndex(): 4 writableBytes(): 0 isWritable(): false maxWritableBytes(): 0

duplicate() 淺層復(fù)制

duplicate() 返回的是源ByteBuf 的整個(gè)對(duì)象的一個(gè)淺層復(fù)制,包括如下內(nèi)容:

  • duplicate() 會(huì)創(chuàng)建自己的讀寫(xiě)指針,但是值與源ByteBuf 的讀寫(xiě)指針相同;
  • duplicate() 不會(huì)改變?cè)?ByteBuf 的引用計(jì)數(shù)
  • duplicate() 不會(huì)拷貝 源ByteBuf 的底層數(shù)據(jù)

duplicate() 和slice() 方法,都是淺層復(fù)制。不同的是,slice() 方法是切取一段的淺層復(fù)制,duplicate() 是整個(gè)的淺層復(fù)制。

淺層復(fù)制的問(wèn)題

? 淺層復(fù)制方法不會(huì)拷貝數(shù)據(jù),也不會(huì)改變 ByteBuf 的引用計(jì)數(shù),這就會(huì)導(dǎo)致一個(gè)問(wèn)題。

? 在源 ByteBuf 調(diào)用 release() 之后,引用計(jì)數(shù)為零,變得不能訪問(wèn)。這個(gè)時(shí)候,源 ByteBuf 的淺層復(fù)制實(shí)例,也不能進(jìn)行讀寫(xiě)。如果再對(duì)淺層復(fù)制實(shí)例進(jìn)行讀寫(xiě),就會(huì)報(bào)錯(cuò)。

? 因此,在調(diào)用淺層復(fù)制實(shí)例時(shí),可以通過(guò)調(diào)用一次 retain() 方法 來(lái)增加引用,表示它們對(duì)應(yīng)的底層的內(nèi)存多了一次引用,引用計(jì)數(shù)為2,在淺層復(fù)制實(shí)例用完后,需要調(diào)用兩次 release() 方法,將引用計(jì)數(shù)減一,不影響源ByteBuf的內(nèi)存釋放。

寫(xiě)在最后

? 至此為止,終于完成ByteBuf的具體使用B介紹。

? 如果想知道ByteBuf的分配、釋放, 請(qǐng)看:

? 第一篇:ByteBuf的分配、釋放和如何避免內(nèi)存泄露


瘋狂創(chuàng)客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 億級(jí)流量】實(shí)戰(zhàn) 開(kāi)源項(xiàng)目實(shí)戰(zhàn)

  • Netty 源碼、原理、JAVA NIO 原理
  • Java 面試題 一網(wǎng)打盡
  • 瘋狂創(chuàng)客圈 【 博客園 總?cè)肟?】


轉(zhuǎn)載于:https://www.cnblogs.com/crazymakercircle/p/9979897.html

與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Netty ByteBuf(图解之 2)| 秒懂的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。