Netty学习——源码篇10 Netty内存分配ByteBuf基础

1 初始ByteBuf

        ByteBuf是Netty整个结构中最为底层的模块,主要负责把数据从底层I/O读取到ByteBuf,然后传递给应用程序,应用程序处理完成后再把数据封装成ByteBuf写回I/O。所以,ByteBuf是直接与底层打交道的一层抽象。

2 ByteBuf的基本结构

        Netty官方对ByteBuf的描述,具体如下:

        从上面ByteBuf的结构来看,发现ByteBuf有三个非常重要的指针,分别是readerIndex(记录读指针的开始位置)、writerIndex(记录写指针的开始位置)和capacity(缓冲区的总长度),三者的关系是:readerIndex <= writerIndex <= capacity。从0到readerIndex为discardable bytes,表示是无效的;从readerIndex 到 writerIndex 为readable bytes,表示可读数据区;从writerIndex 到capacity位writable bytes,表示这段区间空闲,可以往里面写数据。除了这三个指针,ByteBuf里面其实还有一个指针maxCapacity,它相当于ByteBuf扩容的最大阈值,相应代码如下。

    /*** Returns the maximum allowed capacity of this buffer.  If a user attempts to increase the* capacity of this buffer beyond the maximum capacity using {@link #capacity(int)} or* {@link #ensureWritable(int)}, those methods will raise an* {@link IllegalArgumentException}.*/public abstract int maxCapacity();

        这个maxCapacity指针可以看作是指向capacity之后的这段区间,Netty发现writable bytes写数据超出空间大小时,ByteBuf会提前自动扩容,扩容之后,就有了足够的空间来写数据,同时capacity也会同步更新,maxCapacity就是扩容后capacity的最大值。

3 ByteBuf的重要API

        看一下ByteBuf的基本API,主要包括read()  write()  set()  mark() reset()等方法。请看下表

        在Netty中,ByteBuf大部分功能是在AbstractByteBuf中实现的。

public abstract class AbstractByteBuf extends ByteBuf {//读指针int readerIndex;//写指针int writerIndex;//mark之后的读指针private int markedReaderIndex;//mark之后的写指针private int markedWriterIndex;//最大容量private int maxCapacity;...
}

        下面来看基本读写的骨架代码实现。例如,几个基本的判断读写区间的API,具体实现代码如下:

public abstract class AbstractByteBuf extends ByteBuf {
@Overridepublic boolean isReadable() {return writerIndex > readerIndex;}@Overridepublic boolean isReadable(int numBytes) {return writerIndex - readerIndex >= numBytes;}@Overridepublic boolean isWritable() {return capacity() > writerIndex;}@Overridepublic boolean isWritable(int numBytes) {return capacity() - writerIndex >= numBytes;}@Overridepublic int readableBytes() {return writerIndex - readerIndex;}@Overridepublic int writableBytes() {return capacity() - writerIndex;}@Overridepublic int maxWritableBytes() {return maxCapacity() - writerIndex;}@Overridepublic ByteBuf markReaderIndex() {markedReaderIndex = readerIndex;return this;}@Overridepublic ByteBuf resetReaderIndex() {readerIndex(markedReaderIndex);return this;}@Overridepublic ByteBuf markWriterIndex() {markedWriterIndex = writerIndex;return this;}@Overridepublic ByteBuf resetWriterIndex() {writerIndex = markedWriterIndex;return this;}
}

4 ByteBuf基本分类

        AbstractByteBuf有很多子类,大致可以从三个维度进行分类。

        1、Pooled:池化内存,就是从预先分配好的内存空间中提取一段连续内存封装成一个ByteBuf,分给应用程序使用。

        2、Unsafe:是JDK底层的一个负责I/O操作的对象,可以直接获得对应的内存地址,基于内存地址进行读写操作。

        3、Direct:堆外内存,直接调用JDK底层API进行物理内存分配,不在JVM的堆内存中,需要手动释放。

        综上所述,其实ByteBuf会有6种组合:Pooled(池化内存)和Unpooled(非池化内存);Unsafe和非Unsafe;Heap(堆内存)和Direct(堆外内存)。下图是ByteBuf最重要的继承关系类结构图,通过命名就能一目了然。

        ByteBuf最基本的读写API操作在AbstractByteBuf中已经实现了,其众多子类采用不同的策略来分配内存空间,下表是对重要的几个子类的总结。

 5 ByteBufAllocator内存管理器

        Netty中内存分配有一个顶层的抽象就是ByteBufAllocator,负责分配所有ByteBuf类型的内存。主要有几个重要的API,如下表:

        以上API中为什么没有前面提到的8种类型的内存分配API?下面来看ByteBufAllocator的基本实现类AbstractByteBufAllocator,重点分析主要API的基本实现,比如buffer()方法的代码如下:

public abstract class AbstractByteBufAllocator implements ByteBufAllocator {public ByteBuf buffer() {if (directByDefault) {return directBuffer();}return heapBuffer();}
}

         发现buffer方法中对是否默认支持directBuffer做了判断,如果支持则分配directBuffer,否则分配heapBuffer。

        下面分别来看directBuffer方法和heapBuffer方法的实现,先来看directBuffer方法的代码。

    @Overridepublic ByteBuf directBuffer() {return directBuffer(DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);}@Overridepublic ByteBuf directBuffer(int initialCapacity) {return directBuffer(initialCapacity, Integer.MAX_VALUE);}@Overridepublic ByteBuf directBuffer(int initialCapacity, int maxCapacity) {if (initialCapacity == 0 && maxCapacity == 0) {return emptyBuf;}validate(initialCapacity, maxCapacity);return newDirectBuffer(initialCapacity, maxCapacity);}

        directBuffer方法有多个重载方法,最终会调用newDirectBuffer方法,下面看newDirectBuffer的代码。

protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);

        发现newDirectBuffer方法其实是一个抽象方法,最终,交给AbstractByteBufAllocator的子类来实现。同理再来看heapBuffer方法的代码。

   @Overridepublic ByteBuf heapBuffer() {return heapBuffer(DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);}@Overridepublic ByteBuf heapBuffer(int initialCapacity) {return heapBuffer(initialCapacity, Integer.MAX_VALUE);}@Overridepublic ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {if (initialCapacity == 0 && maxCapacity == 0) {return emptyBuf;}validate(initialCapacity, maxCapacity);return newHeapBuffer(initialCapacity, maxCapacity);}

        发现heapBuffer方法最终是调用newHeapBuffer方法,而newHeapBuffer方法也是抽象方法,具体交给AbstractByteBufAllocator的子类实现。AbstractByteBufAllocator的子类主要有两个:PooledByteBufAllocator和UnpooledByteBufAllocator。AbstractByteBufAllocator的子类实现的类结构图如下:

        分析到这里,已经知道directBuffer,heapBuffer和Pooled和Unpooled的分配规则,那么Unsafe和非Unsafe是如何判别的呢?其实是Netty自动判别的。如果操作系统底层支持Unsafe那就采用Unsafe读写,否则采用非Unsafe读写。可以从UnpooledByteBufAllocator的源码中验证,代码如下:

public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {@Overrideprotected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity): new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);}@Overrideprotected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {ByteBuf buf = PlatformDependent.hasUnsafe() ?UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);return disableLeakDetector ? buf : toLeakAwareBuffer(buf);}
}

        发现在newHeapBuffer方法和newDirectBuffer方法中,分配内存判断PlatformDependent是否支持Unsafe,如果支持则创建Unsafe类型的Buffer,否则创建非Unsafe类型的Buffer,由Netty自动判断。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/585819.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

非关系型数据库之Redis配置与优化

一、关系数据库与非关系型数据库 1.1关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上一般面向于记录。SQL语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#xff0c;用…

【漏洞复现】某科技X2Modbus网关多个漏洞

漏洞描述 最近某科技X2Modbus网关出了一个GetUser的信息泄露的漏洞,但是经过审计发现该系统80%以上的接口均是未授权的,没有添加相应的鉴权机制,以下列举多个未授权接口以及获取相关敏感信息的接口。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律…

Linux中间件(nginx搭建、LNMP服务搭建)

目录 一、安装nginx 第一步、下载nginx的压缩包到Linux中 ​第二步、安装依赖 第三步&#xff1a;安装 nginx 第四步&#xff1a;启动nginx 第五步&#xff1a;测试nginx 二、 nginx的配置文件 nginx.conf文件内容解读 案例&#xff1a;发布多个网站 二、lamp/lnmp …

ARM64架构栈帧以及帧指针FP

文章目录 前言一、arm64架构寄存器简介1.1 异常等级1.2 通用寄存器1.3 ARM64架构ABI 二、ARM64架构函数调用标准2.1 AArch64过程调用标准简介2.2 通用寄存器中的参数 三、demo分析3.1 main函数3.2 funb3.3 funa 四、栈帧总结五、demo演示参考资料 前言 这篇文章描述了 x86_64架…

LC 108.将有序数组转换为二叉搜索树

108.将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&#xff1a; 输入&…

筛选树形菜单时关联其父节点和子节点

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 树形菜单 在很多系统管理/菜单管理中经常会出现下面这样的树形菜单&#xff0c;它是通过前端的Tree组…

如何让光猫4个网口都有网络

一般情况光猫只有LAN1口有网络&#xff0c;LAN2、LAN3和LAN4口都是预留给电视用的&#xff0c;那么如何让这3个网口也有网络呢&#xff1f; 使用场景&#xff1a; 光猫在弱电箱内&#xff0c;弱电箱中有三根网线&#xff08;网线1、网线2和网线3&#xff09;分别接入到了三个房…

宝塔面板 -- 打包前端项目并部署提升访问速度

文章目录 前言一、打包前端项目二、添加PHP项目三、部署打包文件四、开通防火墙五、运行网站总结 前言 在前面写到的文章使用宝塔面板部署前端项目中&#xff0c;并没有将前端项目打包而是直接部署&#xff0c;导致网站访问速度非常慢&#xff0c;加载甚至要十几秒。因此&…

文献学习-25-综合学习和适应性教学:用于病理性胶质瘤分级的多模态知识蒸馏

Comprehensive learning and adaptive teaching: Distilling multi-modal knowledge for pathological glioma grading Authors: Xiaohan Xing , Meilu Zhu , Zhen Chen , Yixuan Yuan Source: Medical Image Analysis 91 (2024) 102990 Key words: 知识蒸馏、模态缺失、胶质瘤…

docker部署DOS游戏

下载镜像 docker pull registry.cn-beijing.aliyuncs.com/wuxingge123/dosgame-web-docker:latestdocker-compose部署 vim docker-compose.yml version: 3 services:dosgame:container_name: dosgameimage: registry.cn-beijing.aliyuncs.com/wuxingge123/dosgame-web-docke…

新增收货地址

目录 &#x1f9c2;1.创建controller层 &#x1f953;2.创建service层 &#x1f32d;3.注意细节 &#x1f37f;4.避免dao数据暴漏 1.创建controller层 controller不做逻辑操作&#xff0c;只接受前端的数据 1.添加Api设置swagger模块名称2.RestController以json形式返回…

Spring Boot项目启动速度优化

1、配置自动配置排除列表&#xff0c;减少启动自动配置扫描&#xff0c;配置项spring.autoconfigure.exclude 2、启动类添加索引注解Indexed&#xff0c;去除启动过程中 Components 的扫描步骤&#xff0c;直接从索引文件读取。 import org.springframework.stereotype.lndexe…