成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

【源起Netty 正傳】升級(jí)版卡車——ByteBuf

Jason_Geng / 2997人閱讀

摘要:之所以稱它為卡車,只因編程思想中有段比喻我們可以把它想象成一個(gè)煤礦,通道是一個(gè)包含煤層數(shù)據(jù)的礦藏,而緩沖器則是派送到礦藏中的卡車。那么升級(jí)版卡車,自然指的就是。結(jié)構(gòu)和功能之所以再次打造了升級(jí)版的緩沖器,顯然是不滿中的某些弊端。

卡車

卡車指的是java原生類ByteBuffer,這兄弟在NIO界大名鼎鼎,與Channel、Selector的鐵三角組合構(gòu)筑了NIO的核心。之所以稱它為卡車,只因《編程思想》中有段比喻:

我們可以把它想象成一個(gè)煤礦,通道(Channel)是一個(gè)包含煤層(數(shù)據(jù))的礦藏,而緩沖器(ByteBuffer)則是派送到礦藏中的卡車。卡車滿載煤炭而歸,我們?cè)購目ㄜ嚿汐@得煤炭。也就是說,我們并沒有直接和通道交互;我們只是和緩沖器交互,并把緩沖器派送到通道。

那么升級(jí)版卡車,自然指的就是ByteBuf。

結(jié)構(gòu)和功能

Netty之所以再次打造了升級(jí)版的緩沖器,顯然是不滿ByteBuffer中的某些弊端。

ByteBuffer長度固定

使用者經(jīng)常需要調(diào)用flip()、rewind()方法調(diào)整position的位置,不方便

API功能有限

ByteBuffer中有三個(gè)重要的位置屬性:position、limit、capacity,一個(gè)寫操作之后大概是這樣的

如若想進(jìn)行讀操作,那么flip()的調(diào)用是少不了的,從圖中不難看出,目前position到limit啥也沒有。
調(diào)用flip()之后則不一樣了(我們不一樣~):

而ByteBuf的人設(shè)則不相同,它的兩個(gè)位置屬性readIndex、writeIndex,分別和讀操作、寫操作相對(duì)應(yīng)。“寫”不操作readIndex,“讀”不操作writeIndex,兩者不會(huì)相互干擾。這里盜幾張圖說明下好了:

初始狀態(tài)

寫入N個(gè)字節(jié)

讀取M個(gè)(M

釋放已讀緩存discardReadBytes

重點(diǎn)在于ByteBuf的read和write相關(guān)方法,已經(jīng)封裝好了對(duì)readIndex、writeIndex位置索引的操作,不需要使用者繁瑣的flip()。且write()方法中,ByteBuf設(shè)計(jì)了自動(dòng)擴(kuò)容,這一點(diǎn)后續(xù)章節(jié)會(huì)進(jìn)行詳細(xì)說明。

功能方面,主要關(guān)注兩點(diǎn):

Derived buffers,類似于數(shù)據(jù)庫視圖。ByteBuf提供了多個(gè)接口用于創(chuàng)建某ByteBuf的視圖或復(fù)制ByteBuf:

duplicate:返回當(dāng)前ByteBuf的復(fù)制對(duì)象,緩沖區(qū)內(nèi)容共享(修改復(fù)制的ByteBuf,原來的ByteBuf內(nèi)容也隨之改變),索引獨(dú)立維護(hù)。

copy:內(nèi)容和索引都獨(dú)立。

slice:返回當(dāng)前ByteBuf的可讀子緩沖區(qū),內(nèi)容共享,索引獨(dú)立。

轉(zhuǎn)換成ByteBuffer
nio的SocketChanel進(jìn)行網(wǎng)絡(luò)操作,還是操作的java原生的ByteBuffer,所以ByteBuf轉(zhuǎn)換成ByteBuffer的需求還是有市場(chǎng)的。

ByteBuffer nioBuffer():當(dāng)前ByteBuf的可讀緩沖區(qū)轉(zhuǎn)換成ByteBuffer,緩沖區(qū)內(nèi)容共享,索引獨(dú)立。需要指出的是,返回后的ByteBuffer無法感知原ByteBuf的動(dòng)態(tài)擴(kuò)展操作。

ByteBuf星系

稱之為“星系”,是因?yàn)锽yteBuf一脈涉及到的類實(shí)在太多了,但多而不亂,歸功于類關(guān)系結(jié)構(gòu)的設(shè)計(jì)。

類關(guān)系結(jié)構(gòu)

依然盜圖:

從內(nèi)存分配角度,ByteBuf可分為兩類

堆內(nèi)存HeapByteBuf字節(jié)緩沖區(qū)

直接內(nèi)存DirectByteBuf字節(jié)緩沖區(qū)

從內(nèi)存回收角度,ByteBuf也可分為兩類:

普通緩沖區(qū)UnpooledByteBuf

池化緩沖區(qū)PooledByteBuf

縱觀該關(guān)繼承節(jié)構(gòu),給我留下的印象就是每層各司其職:讀操作以及其它的一些公共功能由父類實(shí)現(xiàn),差異化功能由子類實(shí)現(xiàn)。

下面聊下筆者感興趣的幾個(gè)點(diǎn)……

AbstractByteBuf的寫操作簇

AbstractByteBuf的寫操作有很多,這里以writeBytes(byte[] src, int srcIndex, int length)方法為例

@Override
public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
    ensureWritable(length);    //一、確??蓪懀瑢?duì)邊界進(jìn)行驗(yàn)證
    setBytes(writerIndex, src, srcIndex, length);    //二、寫入操作,不同類型的子類實(shí)現(xiàn)方式不同
    writerIndex += length;
    return this;
}

注釋部分分別展開看下。

注釋一、確??蓪懀瑢?duì)邊界進(jìn)行驗(yàn)證

跟調(diào)用棧ensureWritable -> ensureWritable0,觀察ensureWritable0方法

final void ensureWritable0(int minWritableBytes) {
    ensureAccessible();    //確保對(duì)象可用
    if (minWritableBytes <= writableBytes()) {
        return;
    }

    if (minWritableBytes > maxCapacity - writerIndex) {
        throw new IndexOutOfBoundsException(String.format(
                "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                writerIndex, minWritableBytes, maxCapacity, this));
    }

    // Normalize the current capacity to the power of 2.
    // 三、計(jì)算擴(kuò)容量
    int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);

    // Adjust to the new capacity.
    capacity(newCapacity);    //四、內(nèi)存分配
}

比較

先對(duì)要寫入的字節(jié)數(shù)minWritableBytes進(jìn)行判斷:如果minWritableBytes < capacity - writeIndex,那么很好,不需要擴(kuò)容;如果minWritableBytes > maxCapacity - writerIndex,也就是要寫入字節(jié)數(shù)超過了允許的最大字節(jié)數(shù),直接拋出越界異常IndexOutOfBoundsException。

眼尖的朋友可能發(fā)現(xiàn)了,兩次用來判斷的上界并不相同——capacity / maxCapacity。maxCapacity是AbstractByteBuf的屬性,而capacity設(shè)定在其子類中。簡單看下UnpooledDirectByteBuf的構(gòu)造函數(shù):

public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    super(maxCapacity);    //為AbstractByteBuf的maxCapacity屬性賦值
    
    /**
     *    ……
     *    省略無關(guān)部分
     */
     
    setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));    //capacity賦值
}

也就是說,ByteBuf的結(jié)構(gòu),可看成這樣:

擴(kuò)容計(jì)算

@Override
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
    if (minNewCapacity < 0) {
        throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expected: 0+)");
    }
    if (minNewCapacity > maxCapacity) {
        throw new IllegalArgumentException(String.format(
                "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
                minNewCapacity, maxCapacity));
    }

    /** 
     *  設(shè)置閥值為4MB
     *  1.如果擴(kuò)展的容量大于閥值,對(duì)擴(kuò)張后的內(nèi)存和最大內(nèi)存進(jìn)行比較:大于最大長度使用最大長度,否則步進(jìn)4M
     *  2.如果需要擴(kuò)展的容量小于閥值,以64進(jìn)行計(jì)數(shù)倍增:64->128->256;為防止倍增過猛,最后與最大值再次進(jìn)行比較
     */
    final int threshold = CALCULATE_THRESHOLD; // 4 MiB page

    if (minNewCapacity == threshold) {
        return threshold;
    }

    // If over threshold, do not double but just increase by threshold.
    if (minNewCapacity > threshold) {
        int newCapacity = minNewCapacity / threshold * threshold;
        if (newCapacity > maxCapacity - threshold) {
            newCapacity = maxCapacity;
        } else {
            newCapacity += threshold;
        }
        return newCapacity;
    }

    // Not over threshold. Double up to 4 MiB, starting from 64.
    int newCapacity = 64;
    while (newCapacity < minNewCapacity) {
        newCapacity <<= 1;
    }

    return Math.min(newCapacity, maxCapacity);
}

具體的擴(kuò)容策略,已拍入注釋中,盡可查看!

注釋二、寫入操作,不同類型的子類實(shí)現(xiàn)方式不同

對(duì)比下UnpooledDirectByteBufUnpooledHeapByteBuf的實(shí)現(xiàn)

UnpooledDirectByteBuf

@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
    checkSrcIndex(index, length, srcIndex, src.length);
    ByteBuffer tmpBuf = internalNioBuffer();    //分配
    tmpBuf.clear().position(index).limit(index + length);
    tmpBuf.put(src, srcIndex, length);
    return this;
}

UnpooledHeapByteBuf

@Override
public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
    checkSrcIndex(index, length, srcIndex, src.length);
    System.arraycopy(src, srcIndex, array, index, length); //分配
    return this;
}

篇幅有限,不展開說了,結(jié)論就是:
UnpooledDirectByteBuf的底層實(shí)現(xiàn)為ByteBuffer.allocateDirect,分配時(shí)復(fù)制體通過buffer.duplicate()獲取復(fù)制體;而UnpooledHeapByteBuf的底層實(shí)現(xiàn)為byte[],分配時(shí)通過System.arraycopy方法拷貝副本。

AbstractReferenceCountedByteBuf

AbstractReferenceCountedByteBuf的名字就挺有意思——“引用計(jì)數(shù)”,一副JVM垃圾回收的即視感。而事實(shí)上,也差不多一個(gè)意思。

看下類屬性:

private static final AtomicIntegerFieldUpdater refCntUpdater =
          AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");

private volatile int refCnt;

以原子方式更新屬性的AtomicIntegerFieldUpdater起了關(guān)鍵作用,將會(huì)對(duì)volatile修飾的refCnt進(jìn)行更新,見retain方法(下面展示的是retain的關(guān)鍵部分retain0):

private ByteBuf retain0(final int increment) {
    int oldRef = refCntUpdater.getAndAdd(this, increment);
    if (oldRef <= 0 || oldRef + increment < oldRef) {
        // Ensure we don"t resurrect (which means the refCnt was 0) and also that we encountered an overflow.
        refCntUpdater.getAndAdd(this, -increment);
        throw new IllegalReferenceCountException(oldRef, increment);
    }
    return this;
}

源碼閱讀很有意思的一點(diǎn)就是能看到些自己不熟悉的類,比如AtomicIntegerFieldUpdater我以前就沒接觸過!

內(nèi)存池

內(nèi)存池可有效的提升效率,道理和線程池、數(shù)據(jù)庫連接池相通,即省去了重復(fù)創(chuàng)建銷毀的過程。

到目前為止,看到的都是ByteBuf中的各Unpooled實(shí)現(xiàn),而池化版的ByteBuf沒怎么提過。為何如此?因?yàn)槌鼗膶?shí)現(xiàn)較復(fù)雜,以我現(xiàn)在的功力尚不能完全掌握透徹。

先聊下內(nèi)存池的設(shè)計(jì)思路,漲漲姿勢(shì):
為了集中集中管理內(nèi)存的分配和釋放,同事提高分配和釋放內(nèi)存時(shí)候的性能,很多框架和應(yīng)用都會(huì)通過預(yù)先申請(qǐng)一大塊內(nèi)存,然后通過提供相應(yīng)的分配和釋放接口來使用內(nèi)存。這樣一來,堆內(nèi)存的管理就被集中到幾個(gè)類或函數(shù)中,由于不再頻繁使用系統(tǒng)調(diào)用來申請(qǐng)和釋放內(nèi)存,應(yīng)用或系統(tǒng)的性能也會(huì)大大提高。 ——節(jié)選自《Netty權(quán)威指南》

Netty的ByteBuf內(nèi)存池也是按照這個(gè)思路搞的。首先,看下官方注釋:

/**
 * Notation: The following terms are important to understand the code
 * > page  - a page is the smallest unit of memory chunk that can be allocated
 * > chunk - a chunk is a collection of pages
 * > in this code chunkSize = 2^{maxOrder} * pageSize
 */

這里面有兩個(gè)重要的概念page(頁)和chunk(塊),chunk管理多個(gè)page組成二叉樹結(jié)構(gòu),大概就是這個(gè)樣子:

選擇二叉樹是有原因的:

/**
 * To search for the first offset in chunk that has at least requested size available we construct a
 * complete balanced binary tree and store it in an array (just like heaps) - memoryMap
 */

為了在chunk中找到至少可用的size的偏移量offset。
繼線性結(jié)構(gòu)后,人們又發(fā)明了樹形結(jié)構(gòu)的意義在于“提升查詢效率”,也同樣是這里選擇二叉樹的原因。

小于一個(gè)page的內(nèi)存,直接在PoolSubpage中分配完成。

某塊內(nèi)存是否分配,將通過狀態(tài)位進(jìn)行標(biāo)識(shí)。

后記

一如既往的啰嗦幾句,最近工作忙,更新文章較慢,希望自己能堅(jiān)持,如發(fā)現(xiàn)問題望大家指正!
thanks..

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/68669.html

相關(guān)文章

  • 源起Netty 正傳Netty Channel

    摘要:搞懂了這部分后,我們將明白在世界中扮演的角色進(jìn)擊的此圖展示的已經(jīng)算是優(yōu)化后的了用到了線程池。多線程將這種處理操作分隔出來,非型操作業(yè)務(wù)操作配備以線程池,進(jìn)化成多線程模型這樣的架構(gòu),系統(tǒng)瓶頸轉(zhuǎn)移至部分。 Channel定位 注意:如無特別說明,文中的Channel都指的是Netty Channel(io.netty.channel) 一周時(shí)間的Channel家族學(xué)習(xí),一度讓我懷疑人生——...

    jindong 評(píng)論0 收藏0
  • Netty 4.x 官方入門指南 [譯]

    摘要:目前為止,我們已經(jīng)完成了一半的工作,剩下的就是在方法中啟動(dòng)服務(wù)器。第一個(gè)通常被稱為,負(fù)責(zé)接收已到達(dá)的。這兩個(gè)指針恰好標(biāo)記著數(shù)據(jù)的起始終止位置。 前言 本篇翻譯自netty官方Get Start教程,一方面能把好的文章分享給各位,另一方面能鞏固所學(xué)的知識(shí)。若有錯(cuò)誤和遺漏,歡迎各位指出。 https://netty.io/wiki/user-gu... 面臨的問題 我們一般使用專用軟件或者...

    劉玉平 評(píng)論0 收藏0
  • Netty ByteBuf 誰負(fù)責(zé)誰釋放

    摘要:轉(zhuǎn)發(fā)自 轉(zhuǎn)發(fā)自 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)...

    Lyux 評(píng)論0 收藏0
  • 對(duì)于 Netty ByteBuf 的零拷貝(Zero Copy) 的理解

    摘要:根據(jù)對(duì)的定義即所謂的就是在操作數(shù)據(jù)時(shí)不需要將數(shù)據(jù)從一個(gè)內(nèi)存區(qū)域拷貝到另一個(gè)內(nèi)存區(qū)域因?yàn)樯倭艘淮蝺?nèi)存的拷貝因此的效率就得到的提升在層面上的通常指避免在用戶態(tài)與內(nèi)核態(tài)之間來回拷貝數(shù)據(jù)例如提供的系統(tǒng)調(diào)用它可以將一段用戶空間內(nèi)存映射到內(nèi) 根據(jù) Wiki 對(duì) Zero-copy 的定義: Zero-copy describes computer operations in which the C...

    ConardLi 評(píng)論0 收藏0
  • Netty ByteBuf

    摘要:提供了作為它的字節(jié)容器但是這個(gè)類使用起來過于復(fù)雜而且也有些繁瑣的的代替品是的的數(shù)據(jù)處理通過兩個(gè)組件暴露下面是的優(yōu)點(diǎn)它可以被用戶自定義的緩沖區(qū)類擴(kuò)展通過內(nèi)置的復(fù)合緩沖區(qū)類型實(shí)現(xiàn)了透明的零拷貝容量可以按需增長在讀和寫這兩種模式之間雀環(huán)不需要調(diào)用 Java NIO 提供了 ByteBuffer 作為它的字節(jié)容器, 但是這個(gè)類使用起來過于復(fù)雜, 而且也有些繁瑣. Netty 的 ByteBuf...

    whataa 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<