Netty 權(quán)威指南筆記(五):ByteBuf 源碼解讀

Netty 權(quán)威指南筆記(五):ByteBuf 源碼解讀

功能介紹

Java 本身提供了 ByteBuffer 類,為什么 Netty 還要搞一個 ByteBuf 類呢绸贡?因?yàn)?ByteBuffer 類有著許多缺點(diǎn):

  1. ByteBuffer 長度固定狮辽,無法動態(tài)伸縮拄丰。
  2. ByteBuffer 只有一個位置指針 position轩缤,讀寫的時(shí)候需要手工調(diào)用 flip 和 rewind 方法進(jìn)行模式轉(zhuǎn)換扬蕊,操作繁瑣渔肩,容易出錯逼友。
  3. 功能太少呀闻,缺少一些高級特性化借。

為了彌補(bǔ)這些不足,Netty 提供了自己的緩沖區(qū)類實(shí)現(xiàn) ByteBuf捡多。有什么特點(diǎn)呢蓖康?

  1. 兩個位置指針協(xié)助緩沖區(qū)的讀寫操作:readerIndex、writerIndex垒手,讀寫之間不需要調(diào)整指針位置蒜焊,大大簡化了讀寫操作。
  2. 可以動態(tài)擴(kuò)容科贬。
  3. 當(dāng)有部分內(nèi)容已經(jīng)讀取完成時(shí)泳梆,可以通過 discard 操作對緩沖區(qū)進(jìn)行整理,在不重新申請內(nèi)存的情況下榜掌,增大可寫字節(jié)數(shù)目优妙。
  4. 支持標(biāo)記和回滾的功能。
  5. 支持在 ByteBuf 中查找某個字符串憎账。
  6. 派生出另一個 ByteBuf:duplicate套硼、copy、slice胞皱。
  7. 轉(zhuǎn)化成標(biāo)準(zhǔn)的 ByteBuffer邪意,這是因?yàn)樵谑褂?NIO 進(jìn)行網(wǎng)絡(luò)讀寫時(shí),操作的對象還是 JDK 標(biāo)準(zhǔn)的 ByteBuffer反砌。
  8. 隨機(jī)讀寫雾鬼。

源碼分析

繼承關(guān)系

ByteBuf 的主要功能類繼承關(guān)系如下圖所示:

ByteBuf 類圖.png

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

  1. 堆內(nèi)存字節(jié)緩沖區(qū) HeapByteBuf:優(yōu)點(diǎn)是內(nèi)存分配和回收速度快宴树,可以被 JVM 自動回收策菜。缺點(diǎn)是,如果進(jìn)行 Socket 的 I/O 讀寫,需要額外做一次內(nèi)存復(fù)制做入,在堆內(nèi)存緩沖區(qū)和內(nèi)核 Channel 之間進(jìn)行復(fù)制冒晰,性能會有一定程度下降同衣。
  2. 直接內(nèi)存字節(jié)緩沖區(qū) DirectByteBuf:非堆內(nèi)存竟块,直接在堆外進(jìn)行分配。相比于堆內(nèi)存耐齐,內(nèi)存分配和回收稍慢浪秘,但是可以減少復(fù)制,提升性能埠况。

兩種內(nèi)存耸携,各有利弊。Netty 最佳實(shí)踐表明:在 I/O 通信線程的讀寫緩沖區(qū)使用 DirectByteBuf辕翰,后端業(yè)務(wù)消息的編解碼模塊使用 HeapByteBuf夺衍,這樣組合可以達(dá)到性能最優(yōu)。

從內(nèi)存回收的角度看喜命,ByteBuf 也分為兩類:基于對象池的 ByteBuf 和普通 ByteBuf沟沙。兩者區(qū)別在于基于對象池的 ByteBuf 可以重用 ByteBuf 對象,它自己維護(hù)了一個內(nèi)存池壁榕,可以循環(huán)利用創(chuàng)建的 ByteBuf矛紫,提升內(nèi)存的使用效率,降低由于高負(fù)載導(dǎo)致的頻繁 GC牌里。內(nèi)存池的缺點(diǎn)是管理和維護(hù)比較復(fù)雜颊咬,使用時(shí)需要更加謹(jǐn)慎。

下面我們對一些關(guān)鍵類進(jìn)行分析和解讀牡辽。

AbstractByteBuf

AbstractByteBuf 都做了哪些事兒呢喳篇?我們先看一下其主要的成員變量:

  1. 讀寫指針。
  2. 用于標(biāo)記回滾的 marked 讀寫指針态辛。
  3. 最大容量 maxCapacity麸澜,用于進(jìn)行內(nèi)存保護(hù)。
  4. 與本 ByteBuf 大小端屬性相反的 ByteBuf:SwappedByteBuf因妙。

我們發(fā)現(xiàn)這里沒有真正存儲數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)痰憎,例如 byte 數(shù)組或 DirectByteBuffer,原因是這里還不知道子類是要基于堆內(nèi)存還是直接內(nèi)存攀涵。

public abstract class AbstractByteBuf extends ByteBuf {
    static final ResourceLeakDetector<ByteBuf> leakDetector = new ResourceLeakDetector<ByteBuf>(ByteBuf.class);

    int readerIndex;
    int writerIndex;
    private int markedReaderIndex;
    private int markedWriterIndex;

    private int maxCapacity;

    private SwappedByteBuf swappedBuf;
}

接下來我們看看讀操作 readBytes 方法铣耘,AbstractByteBuf 類做了什么呢?

  1. 在 checkReadableBytes 方法中以故,檢查入?yún)⒂行浴?/li>
  2. 修改讀指針 readerIndex蜗细。
    @Override
    public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
        checkReadableBytes(length);
        // getBytes 方法未在 AbstractByteBuf 中實(shí)現(xiàn)
        getBytes(readerIndex, dst, dstIndex, length);
        readerIndex += length;
        return this;
    }

    protected final void checkReadableBytes(int minimumReadableBytes) {
        ensureAccessible();
        if (minimumReadableBytes < 0) {
            throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)");
        }
        if (readerIndex > writerIndex - minimumReadableBytes) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
                    readerIndex, minimumReadableBytes, writerIndex, this));
        }
    }

從當(dāng)前 ByteBuf 中復(fù)制數(shù)據(jù)到 dst 是在 getBytes 方法中,該方法未在 AbstractByteBuf 中實(shí)現(xiàn),也是因?yàn)榇藭r(shí)具體如何存儲數(shù)據(jù)尚不確定炉媒。

下面我們看一下寫操作 writeBytes 方法踪区,AbstractByteBuf 負(fù)責(zé)實(shí)現(xiàn)了哪些操作呢?

  1. 有效性檢查吊骤,如果引用計(jì)數(shù) refCnt 為 0缎岗,表示該 ByteBuf 已經(jīng)被回收,不能再寫入白粉。
  2. 輸入?yún)?shù)有效性檢查:要寫入的數(shù)據(jù)量不能小于 0传泊,寫入之后總數(shù)據(jù)量也不能大于最大容量。
  3. 當(dāng)容量不足時(shí)鸭巴,如果尚未超過最大容量眷细,則進(jìn)行擴(kuò)容。
  4. 修改寫指針鹃祖。
    @Override
    public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
        ensureAccessible();
        ensureWritable(length);
        // setBytes 交給子類實(shí)現(xiàn)溪椎。
        setBytes(writerIndex, src, srcIndex, length);
        writerIndex += length;
        return this;
    }

    protected final void ensureAccessible() {
        if (refCnt() == 0) {
            throw new IllegalReferenceCountException(0);
        }
    }

    @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format(
                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        }

        if (minWritableBytes <= writableBytes()) {
            return this;
        }

        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.
        int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);

        // 具體擴(kuò)容操作由子類實(shí)現(xiàn)。
        capacity(newCapacity);
        return this;
    }    

在讀寫操作中恬口,AbstractByteBuf 主要負(fù)責(zé)參數(shù)校驗(yàn)校读、讀寫指針修改,以及寫操作時(shí)的擴(kuò)容計(jì)算楷兽。

除此之外地熄,AbstractByteBuf 還提供了以下功能:

  1. 操作索引:修改讀寫指針、mark & reset芯杀。
  2. 重用緩沖區(qū):discardReadBytes端考。
  3. 丟棄部分?jǐn)?shù)據(jù):skipBytes。因?yàn)閬G棄時(shí)揭厚,只需要修改讀指針即可却特,與數(shù)據(jù)具體如何存儲無關(guān)。

總結(jié):在 AbstractByteBuf 中實(shí)現(xiàn)的是各個子類中通用的功能筛圆。

AbstractReferenceCountedByteBuf

從類名可以看出來裂明,該類主要提供引用計(jì)數(shù)的功能,類似于 JVM 內(nèi)存回收的對象引用計(jì)數(shù)器太援,用于跟蹤對象的分配和回收闽晦,實(shí)現(xiàn)手動控制內(nèi)存回收。

首先提岔,我們看一下其成員變量:

  1. refCnt:記錄對象引用次數(shù)仙蛉。
  2. refCntUpdater:用于對 refCnt 進(jìn)行原子更新。
    private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater;

    static {
        AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> updater =
                PlatformDependent.newAtomicIntegerFieldUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
        if (updater == null) {
            updater = AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
        }
        refCntUpdater = updater;
    }

    private volatile int refCnt = 1;

接下來碱蒙,我們看一下增加引用計(jì)數(shù)的 retain 方法荠瘪。該方法是用 CAS 操作對 refCnt 進(jìn)行加 1夯巷。另外,refCnt 值為 0 或 Integer.MAX_VALUE 值不能再操作哀墓,會拋出異常趁餐。

    @Override
    public ByteBuf retain() {
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, 1);
            }
            if (refCnt == Integer.MAX_VALUE) {
                throw new IllegalReferenceCountException(Integer.MAX_VALUE, 1);
            }
            if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) {
                break;
            }
        }
        return this;
    }

另一個 release 方法表示釋放資源,會將引用計(jì)數(shù) refCnt 減 1篮绰,如果當(dāng)前 refCnt 等于 1后雷,減 1 之后等于 0,表示對象已經(jīng)沒有被引用阶牍,可以被回收了喷面,會調(diào)用 deallocate 方法釋放內(nèi)存星瘾。

    @Override
    public final boolean release() {
        for (;;) {
            int refCnt = this.refCnt;
            if (refCnt == 0) {
                throw new IllegalReferenceCountException(0, -1);
            }

            if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) {
                if (refCnt == 1) {
                    deallocate();
                    return true;
                }
                return false;
            }
        }
    }

在 UnpooledHeapByteBuf 中走孽,釋放內(nèi)存僅僅是把 array 數(shù)組置為 null,剩下的內(nèi)存回收工作交由 JVM 來完成琳状。

    // in UnpooledHeapByteBuf.java
    private byte[] array;
    @Override
    protected void deallocate() {
        array = null;
    }

在 UnpooledDirectByteBuf 中磕瓷,則是調(diào)用 PlatformDependent.freeDirectBuffer 來釋放直接內(nèi)存。

    // in UnpooledDirectByteBuf.java
    @Override
    protected void deallocate() {
        ByteBuffer buffer = this.buffer;
        if (buffer == null) {
            return;
        }

        this.buffer = null;

        if (!doNotFree) {
            freeDirect(buffer);
        }
    }

    protected void freeDirect(ByteBuffer buffer) {
        PlatformDependent.freeDirectBuffer(buffer);
    }    

UnpooledHeapByteBuf

UnpooledHeapByteBuf 是基于堆內(nèi)存進(jìn)行內(nèi)存分配的字節(jié)緩沖區(qū)念逞,它沒有基于對象池實(shí)現(xiàn)困食,意味著每次 I/O 讀寫都會創(chuàng)建一個新的 UnpooledHeapByteBuf 對象,頻繁進(jìn)行內(nèi)存的分配和釋放對性能會有一定的影響翎承,但是相對堆外內(nèi)存的申請和釋放硕盹,成本稍低。

相比于 PooledHeapByteBuf叨咖,不需要自己管理內(nèi)存池瘩例,不容易出現(xiàn)內(nèi)存管理方面的問題,更容易使用和維護(hù)甸各。因此垛贤,在滿足性能的情況下,推薦使用 UnpooledHeapByteBuf趣倾。

首先看一下 UnpooledHeapByteBuf 的成員變量:

  1. 負(fù)責(zé)內(nèi)存分配的 ByteBufAllocator聘惦。
  2. 緩沖區(qū)實(shí)現(xiàn) byte 數(shù)組。
  3. 從 ByteBuf 到 NIO 的 ByteBuffer 的轉(zhuǎn)換對象 tmpNioBuf儒恋。
    private final ByteBufAllocator alloc;
    private byte[] array;
    private ByteBuffer tmpNioBuf;

在將 AbstractByteBuf 的時(shí)候善绎,我們提到 getBytes、capacity 等方法是由子類來實(shí)現(xiàn)的诫尽,這里我們先看看 getBytes 的實(shí)現(xiàn)禀酱,從代碼中可以看出來,是直接調(diào)用 System.arraycopy 進(jìn)行的數(shù)組復(fù)制箱锐。

    @Override
    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
        checkDstIndex(index, length, dstIndex, dst.length);
        System.arraycopy(array, index, dst, dstIndex, length);
        return this;
    }

接下來看一下動態(tài)伸縮的 capacity 方法比勉,主要做了以下幾件事:

  1. 參數(shù)校驗(yàn),newCapacity 不能小于 0,大于 maxCapacity浩聋。
  2. 如果 maxCapacity 大于 oldCapacity 表示擴(kuò)容观蜗,直接申請新的 byte 數(shù)組,進(jìn)行內(nèi)存復(fù)制即可衣洁。
  3. 如果 maxCapacity 小于 oldCapacity 就是縮容了墓捻,同樣申請 byte 數(shù)組。不同的是坊夫,需要根據(jù)讀指針 readerIndex 與 newCapacity 的大小來決定是否需要進(jìn)行內(nèi)存復(fù)制砖第。當(dāng) readerIndex 小于 newCapacity 時(shí),需要復(fù)制內(nèi)存环凿,否則不需要梧兼。
  4. 設(shè)置合適的讀寫指針位置。
  5. 更新緩沖區(qū)字節(jié)數(shù)組引用 array 的值智听。
    @Override
    public ByteBuf capacity(int newCapacity) {
        ensureAccessible();
        if (newCapacity < 0 || newCapacity > maxCapacity()) {
            throw new IllegalArgumentException("newCapacity: " + newCapacity);
        }

        int oldCapacity = array.length;
        if (newCapacity > oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            System.arraycopy(array, 0, newArray, 0, array.length);
            setArray(newArray);
        } else if (newCapacity < oldCapacity) {
            byte[] newArray = new byte[newCapacity];
            int readerIndex = readerIndex();
            if (readerIndex < newCapacity) {
                int writerIndex = writerIndex();
                if (writerIndex > newCapacity) {
                    writerIndex(writerIndex = newCapacity);
                }
                System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
            } else {
                setIndex(newCapacity, newCapacity);
            }
            setArray(newArray);
        }
        return this;
    }

    private void setArray(byte[] initialArray) {
        array = initialArray;
        tmpNioBuf = null;
    }

    public ByteBuf setIndex(int readerIndex, int writerIndex) {
        if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
                    readerIndex, writerIndex, capacity()));
        }
        this.readerIndex = readerIndex;
        this.writerIndex = writerIndex;
        return this;
    }

從 ByteBuf 到 ByteBuffer 的轉(zhuǎn)換羽杰,主要是使用了 ByteBuffer 的 wrap 方法:

    @Override
    public ByteBuffer nioBuffer(int index, int length) {
        ensureAccessible();
        return ByteBuffer.wrap(array, index, length).slice();
    }

PooledByteBuf

PooledByteBuf 是 ByteBuf 的內(nèi)存池實(shí)現(xiàn),應(yīng)用自己實(shí)現(xiàn)的內(nèi)存池管理策略到推,一般和操作系統(tǒng)的內(nèi)存管理策略差不多考赛,往往會更簡單些。PooledByteBuf 內(nèi)存池的分配和釋放莉测,主要通過 PoolArena 來實(shí)現(xiàn)颜骤。比如在 capacity 方法中,最終會使用 arena 的 reallocate 方法來重新分配內(nèi)存捣卤。

    public final ByteBuf capacity(int newCapacity) {
        ensureAccessible();

        // If the request capacity does not require reallocation, just update the length of the memory.
        if (chunk.unpooled) {
            if (newCapacity == length) {
                return this;
            }
        } else {
            if (newCapacity > length) {
                if (newCapacity <= maxLength) {
                    length = newCapacity;
                    return this;
                }
            } else if (newCapacity < length) {
                if (newCapacity > maxLength >>> 1) {
                    if (maxLength <= 512) {
                        if (newCapacity > maxLength - 16) {
                            length = newCapacity;
                            setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
                            return this;
                        }
                    } else { // > 512 (i.e. >= 1024)
                        length = newCapacity;
                        setIndex(Math.min(readerIndex(), newCapacity), Math.min(writerIndex(), newCapacity));
                        return this;
                    }
                }
            } else {
                return this;
            }
        }

        // 最終使用 arena 的 reallocate 方法來重新分配內(nèi)存忍抽。
        chunk.arena.reallocate(this, newCapacity, true);
        return this;
    }

PoolArena 是由多個 PoolChunk 組成的大塊內(nèi)存區(qū)域。

abstract class PoolArena<T> {
    static final int numTinySubpagePools = 512 >>> 4;

    final PooledByteBufAllocator parent;

    private final int maxOrder;
    final int pageSize;
    final int pageShifts;
    final int chunkSize;
    final int subpageOverflowMask;
    final int numSmallSubpagePools;
    private final PoolSubpage<T>[] tinySubpagePools;
    private final PoolSubpage<T>[] smallSubpagePools;

    private final PoolChunkList<T> q050;
    private final PoolChunkList<T> q025;
    private final PoolChunkList<T> q000;
    private final PoolChunkList<T> qInit;
    private final PoolChunkList<T> q075;
    private final PoolChunkList<T> q100;
}    
// PoolChunkList 是 PoolChunk 組成的鏈表
final class PoolChunkList<T> {
    private final PoolArena<T> arena;
    private final PoolChunkList<T> nextList;
    PoolChunkList<T> prevList;

    private final int minUsage;
    private final int maxUsage;

    private PoolChunk<T> head;
}    

每個 PoolChunk 由多個 PoolSubpage 組成腌零。

final class PoolChunk<T> {

    final PoolArena<T> arena;
    final T memory;
    final boolean unpooled;

    private final byte[] memoryMap;
    private final byte[] depthMap;
    private final PoolSubpage<T>[] subpages;
    /** Used to determine if the requested capacity is equal to or greater than pageSize. */
    private final int subpageOverflowMask;
    private final int pageSize;
    private final int pageShifts;
    private final int maxOrder;
    private final int chunkSize;
    private final int log2ChunkSize;
    private final int maxSubpageAllocs;
    /** Used to mark memory as unusable */
    private final byte unusable;

    private int freeBytes;      // 當(dāng)前 chunk 空閑字節(jié)數(shù)目

    PoolChunkList<T> parent;    // 父節(jié)點(diǎn)
    PoolChunk<T> prev;          // 鏈表前一個節(jié)點(diǎn)
    PoolChunk<T> next;          // 鏈表后一個節(jié)點(diǎn)
}

PoolSubpage 負(fù)責(zé)管理一個 Page 的內(nèi)存梯找,通過 bitmap 中的每一位來標(biāo)記每一塊兒內(nèi)存的占用狀態(tài)。

final class PoolSubpage<T> {
    final PoolChunk<T> chunk;
    private final int memoryMapIdx; // 當(dāng)前page在chunk中的id
    private final int runOffset;    // 當(dāng)前page在chunk.memory的偏移量
    private final int pageSize;     // page大小
    private final long[] bitmap;    // 通過對每一個二進(jìn)制位的標(biāo)記來修改一段內(nèi)存的占用狀態(tài)

    PoolSubpage<T> prev;
    PoolSubpage<T> next;

    boolean doNotDestroy;
    int elemSize;
    private int maxNumElems;
    private int bitmapLength;
    private int nextAvail;
    private int numAvail;
}

PooledDirectByteBuf

PooledDirectByteBuf 基于內(nèi)存池實(shí)現(xiàn)益涧,與 UnPooledDirectByteBuf 的唯一區(qū)別就是锈锤,緩沖區(qū)的分配和銷毀策略不同。不僅緩沖區(qū)所需內(nèi)存使用內(nèi)存池分配管理闲询,PooledDirectByteBuf 對象本身久免,也使用 Recycler 管理。 比如 PooledDirectByteBuf 創(chuàng)建示例調(diào)用的是 Recycler 的 get 方法扭弧。

final class PooledDirectByteBuf extends PooledByteBuf<ByteBuffer> {

    private static final Recycler<PooledDirectByteBuf> RECYCLER = new Recycler<PooledDirectByteBuf>() {
        @Override
        protected PooledDirectByteBuf newObject(Handle<PooledDirectByteBuf> handle) {
            return new PooledDirectByteBuf(handle, 0);
        }
    };

    static PooledDirectByteBuf newInstance(int maxCapacity) {
        PooledDirectByteBuf buf = RECYCLER.get();
        buf.setRefCnt(1);
        buf.maxCapacity(maxCapacity);
        return buf;
    }
}    

Recycler 是一個輕量級的對象池阎姥,一個對象池最核心的方法是從池中獲取對象和回收對象到池中,分別對應(yīng)其 get 和 recycle 方法鸽捻。

public abstract class Recycler<T> {
    public final T get() {
        Stack<T> stack = threadLocal.get();
        DefaultHandle<T> handle = stack.pop();
        if (handle == null) {
            handle = stack.newHandle();
            handle.value = newObject(handle);
        }
        return (T) handle.value;
    }    
    public final boolean recycle(T o, Handle<T> handle) {
        DefaultHandle<T> h = (DefaultHandle<T>) handle;
        if (h.stack.parent != this) {
            return false;
        }

        h.recycle(o);
        return true;
    }
}

PooledDirectByteBuf 中的 copy 方法用于復(fù)制一個新的字節(jié)緩沖區(qū)實(shí)例呼巴,該方法首先調(diào)用 PooledByteBufAllocator 的 directBuffer 來生成新的 ByteBuf泽腮,然后復(fù)制數(shù)據(jù)。

    @Override
    public ByteBuf copy(int index, int length) {
        checkIndex(index, length);
        ByteBuf copy = alloc().directBuffer(length, maxCapacity());
        copy.writeBytes(this, index, length);
        return copy;
    }

directBuffer 是在抽象類 AbstractByteBufAllocator 中實(shí)現(xiàn)的衣赶,進(jìn)行參數(shù)校驗(yàn)之后調(diào)用 newDirectBuffer 來獲取 ByteBuf诊赊,該方法由子類來實(shí)現(xiàn)。

    @Override
    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newDirectBuffer(initialCapacity, maxCapacity);
    }

在內(nèi)存池版本 PooledByteBufAllocator 的實(shí)現(xiàn)中府瞄,判斷如果內(nèi)存池 directArena 可用碧磅,則從中獲取,否則自行 new 一個遵馆。

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            if (PlatformDependent.hasUnsafe()) {
                buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }

        return toLeakAwareBuffer(buf);
    }

而在非內(nèi)存池版本 UnpooledByteBufAllocator 中鲸郊,則是直接 new 一個。

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        ByteBuf buf;
        if (PlatformDependent.hasUnsafe()) {
            buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }

輔助類

  1. 內(nèi)存分配相關(guān): ByteBufAllocator 及其子類 UnpooledByteBufAllocator货邓、PooledByteBufAllocator秆撮。
  2. 組合視圖:CompositeByteBuf。
  3. 工具類:ByteBufUtil逻恐。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末像吻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子复隆,更是在濱河造成了極大的恐慌,老刑警劉巖姆涩,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挽拂,死亡現(xiàn)場離奇詭異,居然都是意外死亡骨饿,警方通過查閱死者的電腦和手機(jī)亏栈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宏赘,“玉大人绒北,你說我怎么就攤上這事〔焓穑” “怎么了闷游?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贴汪。 經(jīng)常有香客問我脐往,道長,這世上最難降的妖魔是什么扳埂? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任业簿,我火速辦了婚禮,結(jié)果婚禮上阳懂,老公的妹妹穿的比我還像新娘梅尤。我一直安慰自己柜思,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布巷燥。 她就那樣靜靜地躺著酝蜒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矾湃。 梳的紋絲不亂的頭發(fā)上亡脑,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音邀跃,去河邊找鬼霉咨。 笑死,一個胖子當(dāng)著我的面吹牛拍屑,可吹牛的內(nèi)容都是我干的途戒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼僵驰,長吁一口氣:“原來是場噩夢啊……” “哼喷斋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒜茴,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤星爪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粉私,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽腾,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年诺核,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抄肖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡窖杀,死狀恐怖漓摩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情入客,我是刑警寧澤管毙,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站痊项,受9級特大地震影響锅风,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鞍泉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一皱埠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咖驮,春花似錦边器、人聲如沸训枢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恒界。三九已至,卻和暖如春砚嘴,著一層夾襖步出監(jiān)牢的瞬間十酣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工际长, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耸采,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓工育,卻偏偏與公主長得像虾宇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子如绸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文是Netty文集中“Netty in action”系列的文章嘱朽。主要是對Norman Maurer and M...
    tomas家的小撥浪鼓閱讀 3,177評論 0 7
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司怔接,掛了不少搪泳,但最終還是拿到小米、百度蜕提、阿里森书、京東、新浪谎势、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,184評論 11 349
  • 轉(zhuǎn)自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的貓閱讀 2,277評論 0 22
  • 前奏 https://tech.meituan.com/2016/11/04/nio.html 綜述 netty通...
    jiangmo閱讀 5,842評論 0 13
  • 乙酉年冬杨名,《天下無賊》火也脏榆,其傻根飾者名曰寶強(qiáng)。于機(jī)緣巧合中台谍,得見西北大學(xué)傳播學(xué)院學(xué)生馬蓉须喂,正是春風(fēng)滿面桃花開,寶...
    顧秋水閱讀 692評論 4 8