Netty源碼分析之ByteBuf(一)—ByteBuf中API及類型概述


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內部具體實現,希望對大家能有所幫助,其中如有不足與不正確的地方還望指出與海涵。

 

關注微信公眾號,查看更多技術文章。

 

 

轉載說明:未經授權不得轉載,授權后務必注明來源(注明:來源於公眾號:架構空間, 作者:大凡)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM