gateway io.netty.buffer.poolchunk

在本文中,我們將深入探討Netty中的一個基礎組件——PoolChunk,它是Netty中ByteBuf的一個關鍵實現,負責對ByteBuf進行緩存和管理。我們將從多個方面對該組件進行詳細的闡述,包括使用場景、內部實現細節以及如何調優等。

一、PoolChunk的使用場景

在Netty中,ByteBuf是用來封裝來自網路或磁碟的數據塊的Java NIO緩衝區。PoolChunk的任務是對 ByteBuf進行內存管理和緩存。通常情況下,當我們需要使用ByteBuf時,實際上是基於內存池技術從Linux系統堆內存中申請一塊內存。這時,PoolChunk就會派上用場了。

PoolChunk通過對內存進行劃分與分配,解決了內存碎片及應用程序內存泄漏的問題,在多線程並發操作下也能保證內存分配的線程安全性,是Netty中非常重要的一個組件。

二、PoolChunk的內部實現細節

PoolChunk內部主要包含四個類:PoolChunk、PoolSubpage、PoolArena、PoolThreadCache。

1. PoolChunk

PoolChunk是PoolSubpage的容器。它在初始時被劃分為多個Page,每個Page的大小為16M,然後再將Page劃分為大小均為8K的小塊Subpage,以供應用程序分配內存。

/**
 * PoolChunk 表示 PoolSubpage 的容器
 */
@Sharable
@SuppressWarnings("Duplicates")
class PoolChunk implements PoolChunkMetric {

    ...省略部分代碼...

    /**
     * PoolChunk 對象的構造函數
     */
    PoolChunk(PoolArena arena, T memory, int pageSize, int maxOrder, int pageShifts, int chunkSize) {
        ...省略部分代碼...
        this.subpages = newSubpageArray(maxSubpageAllocs);
        ...省略部分代碼...
    }

    ...省略部分代碼...
}

2. PoolSubpage

PoolSubpage是Chunk中用來分配ByteBuf內存的最小內存單元。它是一塊大小固定的內存塊,大小由PoolChunk的maxOrder參數指定。

PoolSubpage有著非常豐富的狀態,可以按照ByteBuf是否處於訪問狀態,以及ByteBuf的容量是否超過了PoolSubpage的容量,將PoolSubpage分成了多種狀態。

/**
 * 表示 PoolChunk 中的一個小塊內存。一個 Subpage 對象代表了 PoolChunk 中的大小為 pageSize 的一個連續內存塊。
 */
final class PoolSubpage implements PoolSubpageMetric {

    ...省略部分代碼...

    /**
     * Subpage佔用的內存大小
     * 如果PoolSubpage不處於當前MemoryChunk中且沒有被分配,則該變數為0
     */
    int elemSize;

    ...省略部分代碼...

    /**
     * PoolSubpage 所在的 PoolChunk 對象
     */
    PoolChunk chunk;

    /**
     * Subpage 所在的 MemoryMap 中的下標
     */
    int memoryMapIdx;

    /**
     * Subpage 在 MemoryMap 下標所對應的高64位數組中的下標
     */
    int runOffset;

    /**
     * Subpage 分配狀態
     */
    private byte[] bitmap;
    private int bitmapLength;
    private int nextAvail;
    private int numAvail;

    ...省略部分代碼...
}

3. PoolArena

PoolArena是一個PoolChunk的容器。PoolArena中包含了多個PoolChunk,每個Chunk按照一定的大小分配若干個Subpage,以供應用程序申請內存。

PoolArena使用了多種技術來優化內存分配,例如使用ByteBuf的可伸縮緩存,在堆內存分配和釋放上更加高效地使用Java Concurrent API等。

/**
 * 用來管理內存塊的 PoolArena 是 PoolSubpage 和 PoolChunk 的容器
 */
@SuppressWarnings("Duplicates")
abstract class PoolArena implements Resource {

    ...省略部分代碼...

    /**
     * newChunk 的具體實現
     */
    protected abstract PoolChunk newChunk(int pageSize, int maxOrder, int pageShifts, int chunkSize);

    /**
     * newSubpage 的具體實現
     */
    protected abstract PoolSubpage newSubpagePoolHead(int pageSize);

    /**
     * 獲取內存大小對應的 Subpage 序號 
     */
    private int tinyIdx(int normCapacity) {
        return normCapacity >>> tinyShift;
    }

    private int smallIdx(int normCapacity) {
        int tableIdx = 0;
        int i = normCapacity >>> smallShift;
        while (i != 0) {
            i >>>= 1;
            tableIdx++;
        }
        return tableIdx;
    }

    ...省略部分代碼...
}

4. PoolThreadCache

PoolThreadCache是Netty用於高效管理Cache的組件,適用於多線程環境。它用於管理多個線程使用ByteBuf中的Cache。

PoolThreadCache會對內存塊進行整合,提高內存的利用率,並且不會浪費空閑內存,節省內存資源。

@SuppressWarnings("Duplicates")
final class PoolThreadCache extends AtomicInteger {

    ...省略部分代碼...

    /**
     * 嘗試從 threadLocalMap 中獲取池化緩存 PoolThreadLocal。
     * 如果 PoolThreadLocal 被 GC 回收,則重新創建,並存儲到 threadLocalMap 中。
     */
    static PoolThreadCache threadCache(Thread t) {
        ...省略部分代碼...
    }

    ...省略部分代碼...
}

三、PoolChunk的調優方法

由於PoolChunk的內部實現非常複雜,存在著很大的調優空間。接下來,我們將介紹通過調整PoolChunk內存池的大小和Subpage大小,以及調整內存分配器使用線程池的線程數等多種方法來優化內存的使用和性能。

1. 調整PoolChunk內存池的大小

PoolChunk的內存池大小與netty.buffer.pool.chunkSize參數密切相關。這個參數默認是16M,可以通過其它工具調整。通過適當增加或減小內存池的大小,可以更好地滿足不同應用程序對內存的需求。

-Dio.netty.buffer.pool.chunkSize=16777216
或
private static final int CHUNK_SIZE = 16 << 20; // 設置為16M

2. 調整Subpage大小

Subpage大小決定了內存分配的粒度。Netty默認使用的Subpage大小是8KB,可以根據不同應用程序的要求進行調整。

-Dio.netty.buffer.page.size=8192
或
private static final int PAGE_SIZE = 8 * 1024; // 設置為8KB

3. 調整內存分配器使用的線程池的線程數

內存分配線程池的線程數量通常也會影響到Netty應用程序的性能。如果線程池中的線程數量過多,可能會導致線程切換的開銷過大,從而影響整個應用程序的性能;如果線程池中的線程數量過少,可能會導致內存分配的速度過慢。

private static final int MAX_NUM_THREAD = 16; // 內存分配線程池的最大線程數量
private static final int MIN_NUM_THREAD = 4;  // 內存分配線程池的最小線程數量
private static final int AGGREGATE_THRESHOLD = 10;//維持最小線程的時間,避免NIO Selector 喚醒線程失效

EventLoopGroup bossGroup = new NioEventLoopGroup(MIN_NUM_THREAD, createWorkerThreadFactory("netty-boss"));
EventLoopGroup workerGroup = new NioEventLoopGroup(MAX_NUM_THREAD, createWorkerThreadFactory("netty-worker"), AGGREGATE_THRESHOLD);

4. 調整ByteBuf池的容量大小

最後,改變ByteBuf池的容量大小也可以影響內存分配的效率。例如,如果應用程序需要處理大塊數據,則可以增大ByteBuf池的容量,從而提高內存分配的效率;如果應用程序需要處理小塊數據,則可以減小ByteBuf池的容量,從而縮小內存分配的粒度。

/**
 * 用戶指定的每個page的大小. 我們根據這個來對內存進行池化
 */
public static final int DEFAULT_PAGE_SIZE = 8 * 1024;

/**
 * 池化分配器的chunk大小。
 */
public static final int DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024; // Use 4 MiB chunks.

public static final int DEFAULT_MAX_CACHED_BUFFERS_PER_CHUNK = 50;

/**
 * Netty使用的最多的ByteBuf緩存數目。
 */
private static final int DEFAULT_NUM_HEAP_ARENA;
private static final int DEFAULT_NUM_DIRECT_ARENA;

static {
        DEFAULT_NUM_HEAP_ARENA = Math.max(1, Runtime.getRuntime().availableProcessors() * 4 / 5);
        DEFAULT_NUM_DIRECT_ARENA = Math.max(1, Runtime.getRuntime().availableProcessors() * 4 / 5);
    }

結語

本文深入探討了Netty中的一個基礎組件——PoolChunk,詳細介紹了PoolChunk和PoolSubpage的內部實現細節,並針對PoolChunk進行了調優,以提高Netty應用程序的性能。希望通過本文的介紹,能夠對讀者更深入地了解Netty。

原創文章,作者:SGJLL,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/374651.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
SGJLL的頭像SGJLL
上一篇 2025-04-28 13:17
下一篇 2025-04-28 13:17

相關推薦

  • 同時啟動兩個netty服務的實現方法

    本文將介紹如何同時啟動兩個netty服務的具體實現方法。 一、實現思路 為了同時啟動兩個netty服務,我們需要創建兩個不同的Channel,每個Channel都綁定到不同的服務端…

    編程 2025-04-27
  • Java IO流學習總結

    一、IO流概述 Java中的IO(Input/Output)流指的是能夠在程序中讀寫數據的一類流。Java中的IO流分為位元組流和字元流兩種類型,其中位元組流以位元組為單位進行讀寫操作,…

    編程 2025-04-23
  • 深入理解 Gateway 配置

    一、Gateway 的作用 Gateway 是 API 網關的核心部件。其作用是將外部請求整合到內部API,從而保證系統的安全和穩定。同時,Gateway 還可以對請求進行鑒權和限…

    編程 2025-04-23
  • 深入剖析IO異常(IOException)

    一、IO異常概述 IOException(Input/Output Exception)表示輸入輸出異常,該異常是Java IO類庫中最重要的類之一。當Java應用程序在運行時出現…

    編程 2025-04-23
  • netty和tomcat的比較

    一、簡介 Netty和Tomcat都是Java Web伺服器,但它們的設計思想和應用場景不同。 Netty是一個高性能、非同步事件驅動的網路通信框架,可以用於實現WebSocket服…

    編程 2025-04-20
  • Linux下磁碟IO的監控與優化

    一、監控磁碟IO工具:iostat iostat是linux下的經典監控工具之一,可以監控系統磁碟I/O、CPU使用情況、網路等系統信息。使用iostat進行磁碟IO監控,需要在終…

    編程 2025-04-12
  • Gateway過濾器詳解

    一、概述 Gateway是Spring Cloud中的一部分,提供了一種靈活的、可擴展的方式來構建API網關,是整個微服務架構的入口和出口。Gateway過濾器位於Gateway請…

    編程 2025-04-12
  • 深入理解Netty粘包

    一、什麼是粘包 網路通信中消息的傳輸有兩個重要的問題,一個是粘包,一個是拆包。 粘包的概念就是發送方發送的多個小數據包被接收方一次性收到,這就像是把多個包「粘」在了一起。 造成粘包…

    編程 2025-04-12
  • Knife4j Gateway使用詳解

    Knife4j Gateway是一款開源、免費的API文檔管理工具,基於SpringBoot2.x、Swagger2、Bootstrap、Vue.js等技術實現,提供了前後端完全分…

    編程 2025-02-05
  • 深入解析io.minio

    一、MinIO概述 MinIO是一款高性能、分散式的對象存儲系統,它是使用Go語言編寫的,允許用戶使用公共雲、私有雲或裸機部署使用。MinIO支持多租戶、跨地域複製、故障轉移、數據…

    編程 2025-02-01

發表回復

登錄後才能評論