Netty的bytebuf详解

news2024/12/23 9:49:18

ByteBuf
ByteBuf是对nio中ByteBuffer的增强。主要的增强点就是ByteBuf它可以动态调整容量大小,当要存储的数据超过了当前容量的上限就会进行扩容,扩容的上限是多少?扩容机制是什么?请跟着本文往下看。对了,还有一个增强就是byteBuf不用和ByteBuffer一样进行读写模式的切换,ByteBuffer中读与写是共用一个指针,而ByteBuf既有读指针,也有写指针。


创建
ByteBufAllocator类中有一个默认实现,我们可以使用这个默认实现来创建一个Bytebuf

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;

public class ByteBufCreateTest {
    public static void main(String[] args) {
        ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
    }
}

这里可以指定ByteBuf的容量,可以传一个int,如果不指定的话默认是256。

这里比nio好的一个地方就是,netty的Bytebuf容量的可以动态扩容的,而nio的ByteBuffer指定了就不能动了。

然后往ByteBuf中添加数据,验证是否会扩容

public static void main(String[] args) {
    // 创建一个Bytebuf,默认创建的容量是256
    ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
    System.out.println("添加数据前:" + buffer);
    // 往Bytebuf中写数据
    StringBuilder stringBuilder = new StringBuilder();
    // 故意超过初始容量,验证是否会自动扩容
    for (int i = 0; i < 300; i++) {
        stringBuilder.append("a");
    }
    // 将数据写入ByteBuf
    buffer.writeBytes(stringBuilder.toString().getBytes());
    System.out.println("添加数据后:" + buffer);
}

输出结果为:PooledUnsafeDirectByteBuf(读指针 写指针 容量)

添加数据前:PooledUnsafeDirectByteBuf(ridx: 0, widx: 0, cap: 256)

添加数据后:PooledUnsafeDirectByteBuf(ridx: 0, widx: 300, cap: 512)

上面的方式只是平时可以这样创建Bytebuf,我们一般情况下都是在Handler中创建ByteBuf,建议使用下面的方式创建ByteBuf
在这里插入图片描述

 

内存分配与池化
ByteBuf支持堆内存,也支持直接内存。

分配堆内存 ByteBufAllocator.DEFAULT.heapBuffer();

分配直接内存 ByteBufAllocator.DEFAULT.directBuffer();

堆内存的分配效率高,但是读写效率低。直接内存读写效率高。nettty默认采用的是直接内存,也就是上面创建ByteBuf时直接使用的

ByteBufAllocator.DEFAULT.buffer(); 获取的是直接内存。

netty中的ByteBuf支持一种池化的管理,netty默认情况下获取的ByteBuf都是从池中获取的,如果不想要从池中获取需要在idea启动配置的地方加一个-Dio.netty.allocator.type=upooled参数

在这里插入图片描述

 

ByteBuf组成

在这里插入图片描述

 

刚开始,读写两个指针都在0的位置。

ByteBuf有读指针和写指针,不像nio中的ByteBuffer公用一个指针 然后还要进行读写模式切换。

Bytebuf如果容量不够了可以动态扩容,但最大容量不能超过int的最大值。

上图中的四块区域分为:废弃区、可读区、可写区、可扩容区

四个属性为:读指针,写指针,容量,最大容量

扩容规则
如果写入的数据没有超过512,则选择的是下一个16的整数倍,例如现在容量是12,进行一次扩容会变为16,如果又不够了就会变为32

而如果写入后的数据大小超过了512,则选择下一个2n。例如写入后的大小为513,库容后的大小就变为210=1024,因为2^9=512不够

但是扩容不能超过max capacity,否则会报错
 

写入

写入的常用方法如下:

以下的方法会改变写指针位置,还有一类以set开头的方法,也可以写数据,但是不会改变写指针的位置

在这里插入图片描述

读取
buffer.readByte() 每次读取一个字节

buffer.readInt() 每次读取一个整数,也就是四个字节

buffer.markReaderIndex() 为读指针做一个标记,配合下面的方法可以实现重复读取某个数

buffer.resetReaderIndex() 将读指针跳到上一个标记过的地方实现重复读取某个数。

除了上面一些了read开头的方法以外,还有一系列get开头的方法也可以读取数据,只不过get开头的方法不会改变读指针位置。相当于是按索引去获取。

内存回收
ByteBuf有几种实现方式,针对这几种ByteBuf实现的机制也不同

  • UnpooleHeapByteBuf 使用的是jvm内存,只需要等待GC回收内存即可
  • UnPooleDirectByteBuf 使用的是直接内存,需要特殊的方法来回收内存
  • PooledByteBuf 和它的子类使用了池化机制,需要复杂的规则来回收内存
     

 不同的ByteBuf的实现,内存回收也不一样,还好netty提供了统一的接口ReferenceCounted接口,该接口提供了通用的方法来进行上面几种ByteBuf的内存回收,该接口的工作方式是采用引用计数的规则

  • 每个ByteBuf 对象的初始计数为1
  • 调用release() 方法计数减1,如果计数为0则ByteBuf的内存会被回收
  • 调用retain()方法计数加1,表示调用者没用完之前,其它handler即使调用了release()也不会造成回收
  • 当计数为0时,底层内存会被回收,这时即使ByteBuf对象还在,其各个方法均无法正常使用
     

谁应该来调用release()方法嘞?

应该由最后一个使用ByteBuf的入站Handler来调用release()方法,因为可能出现前面一个handler释放掉了ByteBuf,下一个handler就使用不了了。
在这里插入图片描述

 如果入站Handler使用的ByteBuf最后传递给了tail,tail会自动释放掉ByteBuf,但是有可能前面的handler处理了数据,然后传递给下一个handler的不是ByteBuf而是处理后的数据,这样最后的tail由于接受的参数不是ByteBuf,所以也没办法释放。出站Handler最后传递给head也会自动自动释放ByteBuf。
头尾handler释放ByteBuf的源码分析
在TailContext类中的channelRead(),然后再跟进方法,多点几层,然后会找到如下所示的方法。
在这里插入图片描述

零拷贝
应用的场景是我要分别处理一个大的ByteBuf中的几段数据,也就是将这一个ByteBuf拆分为几个小的ByteBuf。当然可以直接创建介个小的ByteBuf,然后进行赋值,但是这样有数据拷贝,有效率问题。而零拷贝就是直接将一个大的ByteBuf拆分为几个小的,这里只是逻辑上的拆分,他们共用同一块内存区域。各个小的ByteBuf有自己的读写指针。
在这里插入图片描述

 

其他几个不常用的方法

duplicate() 截取原始ByteBuf的所有内容,而slice()方法只是截取一部分。底层和原始ByteBuf也是使用的同一块内存,也有自己的读写指针。

copeXXXXX 与零拷贝对应的就是一系列以cope开头的方法,也是创建新的ByteBuf,只是会进行数据拷贝,读写都与原始ByteBuf无关

上面是将一个大的ByteBuf切分为几个小的ByteBuf,下面还有将几个小的ByteBuf零拷贝组合成一个大的ByteBuf。

也就是调用compositeBuffer()创建ByteBuf,然后调用addComponents()方法进行添加。
在这里插入图片描述

 

然后运行,发现数据并没有写入新的ByteBuf中,这是因为addComponents() 可变参数 或者是空参的这个方法默认不会去跳转写指针的位置。需要在该方法的参数1位置加一个boolean类型的参数true就可以了。

buffer.addComponents(true, buf1, buf2); 这个就表示写指针会自动增长。这样就可以正确的将两个ByteBuf组合到一起。

使用这个也需要注意release()的问题。

Unpooled是一个工具类,提供了非池化的 ByteBuf创建、组合、复制等操作。这里有一个关于零拷贝相关的方法

Unpooled.wrappedBuffer(ByteBuf buf...)方法,可以将多个Bytebuf组合成一个Bytebuf,底层使用的是compositeBuffer()方法
 

ByteBuf优势

  • 池化,可以重用池中的Bytebuf
  • 自动扩容,容量不够时会自动扩容,但是不会超过int的最大值
  • 读写指针分离,避免了nio中的切换读写模式
  • 支持链式调用
  • 很多地方体现零拷贝,比如slice、duplicate、compositeBuffer、

一个小知识点:java Socket是全双工的,在任意时刻读写都可以同时进行,即使的阻塞io,读写也可以同时进行,只要分别采用两个线程分别处理读写即可。
 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测

区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测 目录 区间预测 | MATLAB实现基于QRCNN-GRU卷积门控循环单元多变量时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于QRCNN-GRU卷积神经网络结合门控循…

[BUUOJ] [RE] [ACTF新生赛2020] rome1

IDA 好久没写博客了&#xff0c;最近在刷re&#xff0c;这道题是我觉得十分有意义的一道题。故AC后总结分享给大家。不足之处请指正。 分析 直接导入IDA shift F12 双击后按 ctrl x跳转到被调用的函数中&#xff0c;按F5反编译&#xff0c;源代码如下 int func() {int r…

数据结构-线性表-链表

目录 线性表的链式存储结构&#xff1a;一、单向链表的ADT定义二、链表的优缺点 线性表的链式存储结构&#xff1a; 为了表示数据元素ai和其后继元素ai1之间的逻辑关系&#xff0c;对ai来说需存储其本身信息和后继元素的信息&#xff08;存储位置&#xff09;。这两部分组成ai…

Android系统(AOSP)--编译指令篇

目录 一、编译Android系统 二、普通编译指令 三、快速编译指令 四、新建lunch项和编译类型说明 五、Android编译系统的整体架构 六、编译后的输出目录和生成文件 七、Android常用编译命令总结 一、编译Android系统 1.Android系统全编译(Android5.1以后mtk都是这种方式…

裸辞3个月没工作,害怕面试,害怕HR问我的问题,怎么办?

其实裸辞最大的伤害就是很容易导致自己的不自信。 现在可能就是你的低谷期&#xff0c;你需要做的是什么呢&#xff0c;丰富自己。 你要相信&#xff0c;你只是太久没有面试过&#xff0c;生疏了而已。 今天小月带你回到面试场&#xff0c;找回面试最纯正的感觉&#xff01; 面…

火龙果MM32F3273G8P开发板MindSDK开发教程1 - 点亮LED

火龙果MM32F3273G8P-MindSDK开发教程1-点亮LED 1、登录官网下载对应的MindSDK固件 https://mindsdk.mindmotion.com.cn/&#xff0c;然后注册下载mm32F3270的固件即可。 下载完的文件为 plus-f3270_mdk.zip 解压后的文件路径如图&#xff1a; 2、新建LED工程 将下载的plu…

基于张量补全的交通数据复原文献汇总(最新)

由于传感器故障和通信故障等因素导致的交通数据缺失严重制约了ITS的发展与应用。如何准确、高效地恢复缺失数据已成为ITS的一个关键问题。近年来&#xff0c;LRTC&#xff08;低秩张量补全&#xff09;的方法已被广泛应用于交通数据补全。本文将介绍几篇最新的关于交通数据补全…

python3 爬虫相关学习7:初步摸索使用 BeautifulSoup

1 一个爬图片pic的代码的例子 下面这段是爬一些图片pic的代码学写了一段bs的代码&#xff0c;但是马上报错 #E:\work\FangCloudV2\personal_space\2learn\python3\py0001.txtimport requests from bs4 import BeautifulSoupurl"https://movie.douban.com/celebrity/10115…

搭建个人博客

个人网站用处有很多&#xff0c;可以写博客来记录学习过程中的各种事&#xff0c;不管是新知识还是踩坑记录&#xff0c;写完就丢在网站上&#xff0c;方便日后复习&#xff0c;也可以共享给他人&#xff0c;让其他人避免踩雷。 当然也不仅限于技术性的文章&#xff0c;生活中有…

隐马尔可夫模型在数学建模中的应用及MATLAB实现

2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 目录 2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 隐马尔可…

(二)CSharp-数据类型

一、数据类型 1、C#程序是一组类型声明 C 程序是一组函数和数据类型C 程序是一组函数和类C# 程序是一组类型声明 2、类型是一种模板 类型由下面的元素定义&#xff1a; 名称用于保存数据成员的数据结构一些行为及约束条件 3、实例化类型 从某个类型模板创建实际的对象&am…

完败!资深码农 VS 新手+AI;阿里云AI黑客松千万奖金池;手把手教你用AI写小说;微软AI入门课 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 阿里云首届AI黑客马拉松&#xff0c;共享 1000 万美元风投资金池 阿里云官方宣布&#xff0c;将以国内最大AI模型社区「Model Scope 魔…

基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建

基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建 文章目录 基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建1. 传统推荐系统模型2. 协同过滤算法3. 基于双视角图表示学习算法的模型构建3.1 数据输入3.2 双视角交互图的构建3.3 混合偏好传播策略3.4 对于双向意…

H5项目怎么打包成APP

文章目录 前言一、新建5APP项目二、删除不需要的文件三、将H5打包的文件拷贝到当前目录下四、配置APP五、发行-云打包六、安装apk总结 前言 开发uni-app的编辑器HBuilderX可以将H5项目打包成APP&#xff0c;相信很多小伙伴还不知道这个功能&#xff0c;下面将介绍下如何将H5打…

linux MMU内存管理单元

本篇文章简要阐述MMU的概念&#xff0c;以及以段地址的转换过程为例&#xff0c;简单说明MMU将虚拟地址转换成物理地址的过程。更多详细内容请查看《ARM-MMU(中文手册).pdf》。 1、MMU概述 在ARM存储系统中&#xff0c;使用MMU实现虚拟地址到实际物理地址的映射。为何要实现这…

人工智能学习07--pytorch21--目标检测:YOLO系列理论合集(YOLOv1~v3)

如果直接看yolov3论文的话&#xff0c;会发现有好多知识点没见过&#xff0c;所以跟着视频从头学一下。 学习up主霹雳吧啦Wz大佬的学习方法&#xff1a; 想学某个网络的代码时&#xff1a;到网上搜这个网络的讲解 → 对这个网络大概有了印象 → 读论文原文&#xff08; 很多细…

Django实现接口自动化平台(五)httprunner(4.x)介绍【持续更新中】

上一章&#xff1a; Django实现接口自动化平台&#xff08;四&#xff09;解决跨域问题【持续更新中】_做测试的喵酱的博客-CSDN博客 下一章&#xff1a; 一、httpruner介绍 1.1 背景&#xff1a; 之所以学习httpruner的用法&#xff0c;是要把httpruner嵌入我们的自动化平…

全网最全、最新MyBatis框架核心知识

MyBatis框架 1. 软件开发常用结构 MyBatis是操作数据库的&#xff0c;相当于是一个增强的JDBC 1.1 三层架构 三层架构包括&#xff1a; 界面层&#xff08;User Interface layer&#xff09;业务逻辑层&#xff08;Business Logic Layer&#xff09;数据访问层&#xff08;Dat…

Window搭建IOS App自动化测试环境

平台搭建&#xff1a;tidevice&#xff08;Windows逆向通信iOS工具&#xff09;WebDriverAgent&#xff08;iOS通信服务&#xff09;facebook-wda&#xff08;iOS测试框架&#xff09; macOSXcode&#xff1a;在手机上安装WebDriverAgent的时候需要用到&#xff0c;必须要Xcod…

HDFS概述及其优缺点

什么是HDFS&#xff1f; HDFS的全称是hadoop distributed file system&#xff0c;即hadoop的分布式文件系统。 见名知意&#xff0c;它就是用来进行文件存储的。毕竟它是大数据的一个组件&#xff0c;用来存储这种海量的数据。 它是基于03年10月份&#xff0c;谷歌发表的GFS…