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/n/374651.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
SGJLLSGJLL
上一篇 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

发表回复

登录后才能评论