最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Netty分布式ByteBuf中PooledByteBufAllocator剖析

 更新時間:2022年03月28日 15:26:32   作者:向南是個萬人迷  
這篇文章主要為大家介紹了Netty分布式ByteBuf剖析PooledByteBufAllocator簡述,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

上一小節(jié)簡單介紹了ByteBufAllocator以及其子類UnPooledByteBufAllocator的緩沖區(qū)分類的邏輯, 這一小節(jié)開始帶大家剖析更為復(fù)雜的PooledByteBufAllocator, 我們知道PooledByteBufAllocator是通過自己取一塊連續(xù)的內(nèi)存進行ByteBuf的封裝, 所以這里更為復(fù)雜, 在這一小節(jié)簡單講解有關(guān)PooledByteBufAllocator分配邏輯

友情提示:  從這一節(jié)開始難度開始加大, 請各位戰(zhàn)友做好心理準備

PooledByteBufAllocator分配邏輯

PooledByteBufAllocator同樣也重寫了AbstractByteBuf的newDirectBuffer和newHeapBuffer兩個抽象方法, 我們這一小節(jié)以newDirectBuffer為例, 先簡述一下其邏輯

邏輯簡述

首先看UnPooledByteBufAllocator中newDirectBuffer這個方法

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 = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
    }
    return toLeakAwareBuffer(buf);
}

首先 PoolThreadCache cache = threadCache.get() 這一步是拿到一個線程局部緩存對象, 線程局部緩存, 顧明思議, 就是同一個線程共享的一個緩存

threadCache是PooledByteBufAllocator類的一個成員變量, 類型是PoolThreadLocalCache(這兩個非常容易混淆, 切記):

private final PoolThreadLocalCache threadCache;

再看其類型PoolThreadLocalCache的定義:

final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
    @Override
    protected synchronized PoolThreadCache initialValue() {
        final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
        final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
        return new PoolThreadCache(
                heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize, 
                DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
    }
    //代碼省略
}

這里繼承了一個FastThreadLocal類, 這個類相當于jdk的ThreadLocal, 只是性能更快, 有關(guān)FastThreadLocal, 我們在后面的章節(jié)會詳細剖析, 這里我們只要知道, 繼承FastThreadLocal類并且重寫了initialValue方法, 則通過其get方法就能獲得initialValue返回的對象, 并且這個對象是線程共享的

在這里我們看到, 在重寫的initialValue方法中, 初始化了heapArena和directArena兩個屬性之后, 通過new PoolThreadCache()這種方式創(chuàng)建了PoolThreadCache對象

這里注意, PoolThreadLocalCache是一個FastThreadLocal, 而PoolThreadCache才是線程局部緩存, 這兩個類名非常非常像, 千萬別搞混了(我當初讀這段代碼時因為搞混所以懵逼了)

其中heapArena和directArena是分別是用來分配堆和堆外內(nèi)存用的兩個對象, 以directArena為例, 我們看到是通過leastUsedArena(directArenas)這種方式獲得的, directArenas是一個directArena類型的數(shù)組, leastUsedArena(directArenas)這個方法是用來獲取數(shù)組中一個使用最少的directArena對象

directArenas是PooledByteBufAllocator的成員變量, 是在其構(gòu)造方法中初始化的:

public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder, 
                              int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
    //代碼省略
    if (nDirectArena > 0) {
        directArenas = newArenaArray(nDirectArena);
        List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
        for (int i = 0; i < directArenas.length; i ++) {
            PoolArena.DirectArena arena = new PoolArena.DirectArena(
                    this, pageSize, maxOrder, pageShifts, chunkSize);
            directArenas[i] = arena;
            metrics.add(arena);
        }
        directArenaMetrics = Collections.unmodifiableList(metrics);
    } else {
        directArenas = null;
        directArenaMetrics = Collections.emptyList();
    }
}

我們看到這里通過directArenas = newArenaArray(nDirectArena)初始化了directArenas, 其中nDirectArena, 默認是cpu核心數(shù)的2倍, 這點我們可以跟蹤構(gòu)造方法的調(diào)用鏈可以分析到

這樣保證了每一個線程會有一個獨享的arena

我們看newArenaArray(nDirectArena)這個方法:

private static <T> PoolArena<T>[] newArenaArray(int size) {
    return new PoolArena[size];
}

這里只是創(chuàng)建了一個數(shù)組, 默認長度為nDirectArena

繼續(xù)跟PooledByteBufAllocator的構(gòu)造方法, 創(chuàng)建完了數(shù)組, 后面在for循環(huán)中為數(shù)組賦值:

首先通過new PoolArena.DirectArena創(chuàng)建一個DirectArena實例, 然后再為新創(chuàng)建的directArenas數(shù)組賦值

再回到PoolThreadLocalCache的構(gòu)造方法中:

final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
    @Override
    protected synchronized PoolThreadCache initialValue() {
        final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
        final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
        return new PoolThreadCache(
                heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize, 
                DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
    }
    //代碼省略
}

方法最后, 創(chuàng)建PoolThreadCache的一個對象, 我們跟進構(gòu)造方法中:

PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena, 
                int tinyCacheSize, int smallCacheSize, int normalCacheSize, 
                int maxCachedBufferCapacity, int freeSweepAllocationThreshold) {
    //代碼省略
    //保存成兩個成員變量
    this.heapArena = heapArena;
    this.directArena = directArena;
    //代碼省略
}

這里省略了大段代碼, 只需要關(guān)注這里將兩個值保存在PoolThreadCache的成員變量中

我們回到newDirectBuffer中

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 = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
    }
    return toLeakAwareBuffer(buf);
}

簡單分析的線程局部緩存初始化相關(guān)邏輯, 我們再往下跟:

PoolArena<ByteBuffer> directArena = cache.directArena;

通過上面的分析, 這步我們應(yīng)該不陌生, 在PoolThreadCache構(gòu)造方法中將directArena和heapArena中保存在成員變量中, 這樣就可以直接通過cache.directArena這種方式拿到其成員變量的內(nèi)容

從以上邏輯, 我們可以大概的分析一下流程, 通常會創(chuàng)建和線程數(shù)量相等的arena, 并以數(shù)組的形式存儲在PooledByteBufAllocator的成員變量中, 每一個PoolThreadCache創(chuàng)建的時候, 都會在當前線程拿到一個arena, 并保存在自身的成員變量中

PoolThreadCache除了維護了一個arena之外, 還維護了一個緩存列表, 我們在重復(fù)分配ByteBuf的時候, 并不需要每次都通過arena進行分配, 可以直接從緩存列表中拿一個ByteBuf

有關(guān)緩存列表, 我們循序漸進的往下看

在PooledByteBufAllocator中維護了三個值:

1.  tinyCacheSize

2.  smallCacheSize

3.  normalCacheSize

tinyCacheSize代表tiny類型的ByteBuf能緩存多少個

smallCacheSize代表small類型的ByteBuf能緩存多少個

normalCacheSize代表normal類型的ByteBuf能緩存多少個

具體tiny類型, small類型, normal是什么意思, 我們會在后面講解

我們回到PoolThreadLocalCache類中看其構(gòu)造方法:

final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
    @Override
    protected synchronized PoolThreadCache initialValue() {
        final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
        final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
        return new PoolThreadCache(
                heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize, 
                DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
    }
    //代碼省略
}

我們看到這三個屬性是在PoolThreadCache的構(gòu)造方法中傳入的

這三個屬性是通過PooledByteBufAllocator的構(gòu)造方法中初始化的, 跟隨構(gòu)造方法的調(diào)用鏈會走到這個構(gòu)造方法:

public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
    this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, 
            DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
}

這里仍然調(diào)用了一個重載的構(gòu)造方法, 這里我們關(guān)注這幾個參數(shù):

DEFAULT_TINY_CACHE_SIZE,

DEFAULT_SMALL_CACHE_SIZE,

DEFAULT_NORMAL_CACHE_SIZE

這里對應(yīng)著幾個靜態(tài)的成員變量:

private static final int DEFAULT_TINY_CACHE_SIZE;
private static final int DEFAULT_SMALL_CACHE_SIZE;
private static final int DEFAULT_NORMAL_CACHE_SIZE;

我們在static塊中看其初始化過程

static{
    //代碼省略
    DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
    DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
    DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
    //代碼省略
}

在這里我們看到, 這三個屬性分別初始化的大小是512, 256, 64, 這三個屬性就對應(yīng)了PooledByteBufAllocator另外的幾個成員變量, tinyCacheSize, smallCacheSize, normalCacheSize

也就是說, tiny類型的ByteBuf在每個緩存中默認緩存的數(shù)量是512個, small類型的ByteBuf在每個緩存中默認緩存的數(shù)量是256個, normal類型的ByteBuf在每個緩存中默認緩存的數(shù)量是64個

我們再到PooledByteBufAllocator中重載的構(gòu)造方法中:

public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder, 
                              int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
    super(preferDirect);
    threadCache = new PoolThreadLocalCache();
    this.tinyCacheSize = tinyCacheSize;
    this.smallCacheSize = smallCacheSize;
    this.normalCacheSize = normalCacheSize;
    //代碼省略
}

篇幅原因, 這里也省略了大段代碼, 大家可以通過構(gòu)造方法參數(shù)找到源碼中相對的位置進行閱讀

我們關(guān)注這段代碼:

this.tinyCacheSize = tinyCacheSize;
this.smallCacheSize = smallCacheSize;
this.normalCacheSize = normalCacheSize;

在這里將將參數(shù)的

DEFAULT_TINY_CACHE_SIZE,

DEFAULT_SMALL_CACHE_SIZE,

DEFAULT_NORMAL_CACHE_SIZE

的三個值保存到了成員變量

tinyCacheSize,

smallCacheSize,

normalCacheSize

PooledByteBufAllocator中將這三個成員變量初始化之后, 在PoolThreadLocalCache的initialValue方法中就可以使用這三個成員變量的值了

我們再次跟到initialValue方法中

final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
    @Override
    protected synchronized PoolThreadCache initialValue() {
        final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
        final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
        return new PoolThreadCache(
                heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize, 
                DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
    }
    //代碼省略
}

這里就可以在創(chuàng)建PoolThreadCache對象的的構(gòu)造方法中傳入tinyCacheSize, smallCacheSize, normalCacheSize這三個成員變量了

我們再跟到PoolThreadCache的構(gòu)造方法中:

PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena, 
                int tinyCacheSize, int smallCacheSize, int normalCacheSize, 
                int maxCachedBufferCapacity, int freeSweepAllocationThreshold) {
    //代碼省略
    this.freeSweepAllocationThreshold = freeSweepAllocationThreshold;
    this.heapArena = heapArena;
    this.directArena = directArena;
    if (directArena != null) {
        tinySubPageDirectCaches = createSubPageCaches(
                tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
        smallSubPageDirectCaches = createSubPageCaches(
                smallCacheSize, directArena.numSmallSubpagePools, SizeClass.Small);

        numShiftsNormalDirect = log2(directArena.pageSize);
        normalDirectCaches = createNormalCaches(
                normalCacheSize, maxCachedBufferCapacity, directArena);

        directArena.numThreadCaches.getAndIncrement();
    } else {
        //代碼省略
    }
    //代碼省略
    ThreadDeathWatcher.watch(thread, freeTask);
}

其中tinySubPageDirectCaches, smallSubPageDirectCaches, 和normalDirectCaches就代表了三種類型的緩存數(shù)組, 數(shù)組元素是MemoryRegionCache類型的對象, MemoryRegionCache就代表一個ByeBuf的緩存

以tinySubPageDirectCaches為例, 我們看到tiny類型的緩存是通過createSubPageCaches這個方法創(chuàng)建的

這里傳入了三個參數(shù)tinyCacheSize我們之前分析過是512, PoolArena.numTinySubpagePools這里是32(這里不同類型的緩存大小不一樣, small類型是4, normal類型是3) , SizeClass.Tiny代表其類型是tiny類型

我們跟到createSubPageCaches這個方法中

private static <T> MemoryRegionCache<T>[] createSubPageCaches(
        int cacheSize, int numCaches, SizeClass sizeClass) {
    if (cacheSize > 0) {
        //創(chuàng)建數(shù)組, 長度為32
        @SuppressWarnings("unchecked")
        MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
        for (int i = 0; i < cache.length; i++) {
            //每一個節(jié)點是ubPageMemoryRegionCache對象
            cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);
        }
        return cache;
    } else {
        return null;
    }
}

這里首先創(chuàng)建了MemoryRegionCache, 長度是我們剛才分析過的32

然后通過for循環(huán), 為數(shù)組賦值, 賦值的對象是SubPageMemoryRegionCache類型的, SubPageMemoryRegionCache就是MemoryRegionCache類型的子類, 同樣也是一個緩存對象, 構(gòu)造方法中, cacheSize, 就是其中緩存對象的數(shù)量, 如果是tiny類型就是512, sizeClass, 代表其類型, 比如tiny, small或者normal

再簡單跟到其構(gòu)造方法:

SubPageMemoryRegionCache(int size, SizeClass sizeClass) {
    super(size, sizeClass);
}

這里調(diào)用了父類的構(gòu)造方法, 我們繼續(xù)跟進去:

MemoryRegionCache(int size, SizeClass sizeClass) {
     //size會進行規(guī)格化
     this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
     //隊列大小
     queue = PlatformDependent.newFixedMpscQueue(this.size);
     this.sizeClass = sizeClass;
 }

首先會對其進行規(guī)格化, 其實就是查找大于等于當前size的2的冪次方的數(shù), 這里如果是512那么規(guī)格化之后還是512, 然后初始化一個隊列, 隊列大小就是傳入的大小, 如果是tiny, 這里大小就是512

最后并保存其類型

這里我們不難看出, 其實每個緩存的對象, 里面是通過一個隊列保存的, 有關(guān)緩存隊列和ByteBuf之間的邏輯, 后面的小節(jié)會進行剖析

從上面剖析我們不難看出, PoolThreadCache中維護了三種類型的緩存數(shù)組, 每個緩存數(shù)組中的每個值中, 又通過一個隊列進行對象的存儲

當然這里只舉了Direct類型的對象關(guān)系, heap類型其實都是一樣的, 這里不再贅述

這一小節(jié)邏輯較為復(fù)雜, 同學(xué)們可以自己在源碼中跟蹤一遍加深印象

以上就是Netty分布式ByteBuf中PooledByteBufAllocator剖析的詳細內(nèi)容,更多關(guān)于Netty分布式ByteBuf PooledByteBufAllocato的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot集成quartz實現(xiàn)定時任務(wù)

    SpringBoot集成quartz實現(xiàn)定時任務(wù)

    這篇文章主要介紹了如何使用SpringBoot整合Quartz,并將定時任務(wù)寫入庫中(持久化存儲),還可以任意對定時任務(wù)進行如刪除、暫停、恢復(fù)等操作,需要的可以了解下
    2023-09-09
  • idea配置gradle全過程

    idea配置gradle全過程

    安裝Gradle首先需要解壓安裝包到指定目錄,隨后配置環(huán)境變量GRDLE_HOME和GRADLE_USER_HOME,這里的GRADLE_USER_HOME是指文件下載的路徑,安裝后,通過命令行輸入gradle -v來測試是否安裝成功,對于Idea的配置,需要通過File->Setting->Gradle進行
    2024-10-10
  • Android中Parcelable的作用實例解析

    Android中Parcelable的作用實例解析

    這篇文章主要介紹了Android中Parcelable的作用,對于Android初學(xué)者有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2014-08-08
  • SpringBoot返回前端Long類型字段丟失精度問題及解決方案

    SpringBoot返回前端Long類型字段丟失精度問題及解決方案

    Java服務(wù)端返回Long整型數(shù)據(jù)給前端,JS會自動轉(zhuǎn)換為Number類型,本文主要介紹了SpringBoot返回前端Long類型字段丟失精度問題及解決方案,感興趣的可以了解一下
    2024-03-03
  • Springboot中yml文件沒有葉子圖標的解決

    Springboot中yml文件沒有葉子圖標的解決

    這篇文章主要介紹了Springboot中yml文件沒有葉子圖標的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • java如何獲取request中json數(shù)據(jù)

    java如何獲取request中json數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于java如何獲取request中json數(shù)據(jù)的相關(guān)資料,文中通過代碼示例以及圖文將獲取的方法介紹的非常詳細,對大家學(xué)習(xí)或者使用java具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-08-08
  • mybatis多層嵌套resultMap及返回自定義參數(shù)詳解

    mybatis多層嵌套resultMap及返回自定義參數(shù)詳解

    這篇文章主要介紹了mybatis多層嵌套resultMap及返回自定義參數(shù)詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • java中HashMap的七種遍歷方式小結(jié)

    java中HashMap的七種遍歷方式小結(jié)

    本文主要介紹了java中HashMap的七種遍歷方式小結(jié),包括迭代器,For Each,Lambda,Streams API等,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • Spring?boot詳解fastjson過濾字段為null值如何解決

    Spring?boot詳解fastjson過濾字段為null值如何解決

    這篇文章主要介紹了解決Spring?boot中fastjson過濾字段為null值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Java調(diào)用windows系統(tǒng)的CMD命令并啟動新程序

    Java調(diào)用windows系統(tǒng)的CMD命令并啟動新程序

    本文教你如何使用java程序調(diào)用windows系統(tǒng)的CMD命令啟動新程序方法,需要的朋友可以參考下
    2023-05-05

最新評論

三原县| 山东省| 胶州市| 东乡县| 内丘县| 阳信县| 婺源县| 丰宁| 盐池县| 西和县| 乌审旗| 台北市| 南丹县| 迁安市| 甘泉县| 鸡西市| 江达县| 武清区| 乐业县| 尼玛县| 清河县| 象山县| 湘潭县| 那坡县| 达州市| 石嘴山市| 密山市| 赤城县| 中超| 肇源县| 榆中县| 萨嘎县| 福贡县| 惠州市| 古浪县| 黄石市| 建德市| 页游| 田东县| 陇川县| 台州市|