【Netty学习】七、详解ByteBuf缓冲区

news2025/2/25 13:53:18

七、详解ByteBuf缓冲区

为了确保引用计数不会混乱,在Netty的业务处理器开发过程中,应该坚持一个原则:retain和release方法应该结对使用。简单地说,在一个方法中,调用了retain,就应该调用一次release。

        public void handlMethodA(ByteBufbyteBuf) {
            byteBuf.retain();
            try {
                handlMethodB(byteBuf);
            } finally {
                byteBuf.release();
            }
        }

如果retain和release这两个方法,一次都不调用呢?则在缓冲区使用完成后,调用一次release,就是释放一次。例如在Netty流水线上,中间所有的Handler业务处理器处理完ByteBuf之后直接传递给下一个,由最后一个Handler负责调用release来释放缓冲区的内存空间。
当引用计数已经为0, Netty会进行ByteBuf的回收。分为两种情况:(1)Pooled池化的ByteBuf内存,回收方法是:放入可以重新分配的ByteBuf池子,等待下一次分配。(2)Unpooled未池化的ByteBuf缓冲区,回收分为两种情况:如果是堆(Heap)结构缓冲,会被JVM的垃圾回收机制回收;如果是Direct类型,调用本地方法释放外部内存(unsafe.freeMemory)。

7. ByteBuf的Allocator分配器

Netty通过ByteBufAllocator分配器来创建缓冲区和分配内存空间。Netty提供了ByteBufAllocator的两种实现:PoolByteBufAllocator和UnpooledByteBufAllocator。
PoolByteBufAllocator(池化ByteBuf分配器)将ByteBuf实例放入池中,提高了性能,将内存碎片减少到最小;这个池化分配器采用了jemalloc高效内存分配的策略,该策略被好几种现代操作系统所采用。
UnpooledByteBufAllocator是普通的未池化ByteBuf分配器,它没有把ByteBuf放入池中,每次被调用时,返回一个新的ByteBuf实例;通过Java的垃圾回收机制回收。
为了验证两者的性能,大家可以做一下对比试验:
(1)使用UnpooledByteBufAllocator的方式分配ByteBuf缓冲区,开启10000个长连接,每秒所有的连接发一条消息,再看看服务器的内存使用量的情况。
实验的参考结果:在短时间内,可以看到占到10GB多的内存空间,但随着系统的运行,内存空间不断增长,直到整个系统内存被占满而导致内存溢出,最终系统宕机。
(2)把UnpooledByteBufAllocator换成PooledByteBufAllocator,再进行试验,看看服务器的内存使用量的情况。
实验的参考结果:内存使用量基本能维持在一个连接占用1MB左右的内存空间,内存使用量保持在10GB左右,经过长时间的运行测试,我们会发现内存使用量都能维持在这个数量附近,系统不会因为内存被耗尽而崩溃。
在Netty中,默认的分配器为ByteBufAllocator.DEFAULT,可以通过Java系统参数(System Property)的选项io.netty.allocator.type进行配置,配置时使用字符串值:“unpooled”, “pooled”。
不同的Netty版本,对于分配器的默认使用策略是不一样的。在Netty 4.0版本中,默认的分配器为UnpooledByteBufAllocator而在Netty 4.1版本中,默认的分配器为PooledByteBufAllocator。现在PooledByteBufAllocator已经广泛使用了一段时间,并且有了增强的缓冲区泄漏追踪机制。因此,可以在Netty程序中设置启动器Bootstrap的时候,将PooledByteBufAllocator设置为默认的分配器。

        ServerBootstrap b = new ServerBootstrap()
        //....
        //4 设置通道的参数
        b.option(ChannelOption.SO_KEEPALIVE, true);
        b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        //....

内存管理的策略可以灵活调整,这是使用Netty所带来的又一个好处。只需一行简单的配置,就能获得到池化缓冲区带来的好处。在底层,Netty为我们干了所有“脏活、累活”!这主要是因为Netty用到了Java的Jemalloc内存管理库
使用分配器分配ByteBuf的方法有多种。下面列出主要的几种:

        package com.crazymakercircle.netty.bytebuf;
        //...
        public class AllocatorTest {
            @Test
            public void showAlloc() {
              ByteBuf buffer = null;
              //方法一:分配器默认分配初始容量为9,最大容量100的缓冲区
              buffer = ByteBufAllocator.DEFAULT.buffer(9, 100);
              //方法二:分配器默认分配初始容量为256,最大容量Integer.MAX_VALUE的缓冲区
              buffer = ByteBufAllocator.DEFAULT.buffer();
              //方法三:非池化分配器,分配基于Java的堆(Heap)结构内存缓冲区
              buffer = UnpooledByteBufAllocator.DEFAULT.heapBuffer();
              //方法四:池化分配器,分配基于操作系统管理的直接内存缓冲区
              buffer = PooledByteBufAllocator.DEFAULT.directBuffer();
              //…..其他方法
            }
        }

如果没有特别的要求,使用第一种或者第二种分配方法分配缓冲区即可。

8. ByteBuf缓冲区的类型

根据内存的管理方不同,分为堆缓存区和直接缓存区,也就是Heap ByteBuf和Direct ByteBuf。另外,为了方便缓冲区进行组合,提供了一种组合缓存区
image.png
上面三种缓冲区的类型,无论哪一种,都可以通过池化(Pooled)、非池化(Unpooled)两种分配器来创建和分配内存空间。
下面对Direct Memory(直接内存)进行一下特别的介绍:

  • Direct Memory不属于Java堆内存,所分配的内存其实是调用操作系统malloc()函数来获得的;由Netty的本地内存堆Native堆进行管理。
  • Direct Memory容量可通过-XX:MaxDirectMemorySize来指定,如果不指定,则默认与Java堆的最大值(-Xmx指定)一样。注意:并不是强制要求,有的JVM默认Direct Memory与-Xmx无直接关系。
  • Direct Memory的使用避免了Java堆和Native堆之间来回复制数据。在某些应用场景中提高了性能。
  • 在需要频繁创建缓冲区的场合,由于创建和销毁Direct Buffer(直接缓冲区)的代价比较高昂,因此不宜使用Direct Buffer。也就是说,Direct Buffer尽量在池化分配器中分配和回收。如果能将Direct Buffer进行复用,在读写频繁的情况下,就可以大幅度改善性能。
  • 对Direct Buffer的读写比Heap Buffer快,但是它的创建和销毁比普通Heap Buffer慢。
  • 在Java的垃圾回收机制回收Java堆时,Netty框架也会释放不再使用的Direct Buffer缓冲区,因为它的内存为堆外内存,所以清理的工作不会为Java虚拟机(JVM)带来压力。注意一下垃圾回收的应用场景:(1)垃圾回收仅在Java堆被填满,以至于无法为新的堆分配请求提供服务时发生;(2)在Java应用程序中调用System.gc()函数来释放内存。

Heap ByteBuf和Direct ByteBuf两类缓冲区的使用。它们有以下几点不同:
· 创建的方法不同:Heap ByteBuf通过调用分配器的buffer()方法来创建;而Direct ByteBuf的创建,是通过调用分配器的directBuffer()方法。
· Heap ByteBuf缓冲区可以直接通过array()方法读取内部数组;而Direct ByteBuf缓冲区不能读取内部数组。
· 可以调用hasArray()方法来判断是否为Heap ByteBuf类型的缓冲区;如果hasArray()返回值为true,则表示是Heap堆缓冲,否则就不是。
· Direct ByteBuf要读取缓冲数据进行业务处理,相对比较麻烦,需要通过getBytes/readBytes等方法先将数据复制到Java的堆内存,然后进行其他的计算。

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

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

相关文章

视图存储过程存储函数

文章目录视图常见数据库对象视图概述为什么使用视图?视图的理解创建视图创建单表视图创建多表联合视图基于视图创建视图查看视图更新视图的数据一般情况不可更新的视图修改、删除视图修改视图删除视图总结视图优点视图不足存储过程&存储函数存储过程概述理解分类…

NFT Insider #84:The Sandbox与华纳音乐集团合作举办全世界最大的DemoDrop,英超联赛签署NFT协议

引言:NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品,浓缩每周NFT新闻,为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据,艺术新闻类,游戏新闻类,虚拟世界类&#…

[论文分享] How could Neural Networks understand Programs?

前言 读一篇 ICML 2021 的论文How could Neural Networks understand Programs? 程序语义理解是程序设计语言处理(PLP)的一个基本问题。最近基于NLP预训练技术学习代码表示的工作,推动了该方向的前沿。然而,PL和NL的语义有着本质的区别。忽略这些&…

CPP----精选常识100例

1 静态全局变量的作用域 本文件 2 判断一个程序是C还是C编译的 #ifdef __cpluspluscout << "c"; #else cout << "c"; #endif3 C函数传递方式 值传递&#xff0c;引用传递&#xff0c;指针传递 4 虚函数定义及用法 虚函数是C中用于实现多态(p…

vue2 a-tree-select树形结构-懒加载(无限子级)---笔记

实现效果 思维导图 HTML代码&#xff1a;treeData是绑定的数组&#xff0c;onLoadData是懒加载函数 <a-tree-select style"width: 100%; margin-left: 20px" tree-data-simple-mode multiplelabelInValueplaceholder"请选择…" v-decorator"[lea…

史上最详细的KMP算法教程,看这一篇就够了

&#x1f9d1;‍&#x1f4bb; 文章作者&#xff1a;Iareges &#x1f517; 博客主页&#xff1a;https://blog.csdn.net/raelum ⚠️ 转载请注明出处 目录一、BF算法二、KMP算法2.1 字符串基础2.2 next数组2.3 KMP的实现2.4 next数组的生成三、改进的KMP算法3.1 nextval数组3.…

turf.js实现行政区(多边形)图形合并边界提取,掩膜等效果

在做前端行政区展示的时候,可能经常会遇到这样的需求,就是给定一个行政区比如杭州市各个区,县的行政区边界图形,但是我们现在需要一个杭州市的行政区边界,我们是否可以通过前端合并这些行政区,答案当然是可以的,我们可以使用turf.js来实现这个需求。 turf官网:Turf.js…

纯滞后系统的数字Smith预估控制-2

在纯滞后系统的数字Smith预估控制-1的基础上进行Simulink仿真。采用 Simulink 进行数字化仿真&#xff0c;按Smith算法设计Simulink模块。在PI控制中&#xff0c;kp0.5&#xff0c;ki0.01。其响应结果如图1和图2所示。图1 Smith阶跃响应结果图2 只采用PI控制时的阶跃响应结果初…

CDA Level Ⅱ 模拟题(一)

单选1 练习题 【单选题】1/20 一项针对某城市小微企业税收扶持和税收种类的调查&#xff0c;本打算调查500个企业&#xff0c;但忽然发现税务中心数据库中已存有这项调查数据&#xff0c;并且可以有权限获取这份数据&#xff0c;请问这是什么类型的调查方式&#xff1f; A.分层…

flask计算pin码

Flask debug模式算pin码_Ys3ter的博客-CSDN博客_flask pin码 可以参考这个链接 ctfshow801 然后这张图非常的重要 也就是我们需要上面的各个因素&#xff0c;然后获得ping码&#xff0c;也就是console的密码&#xff0c;就可以自己输出命令 然后会有两个脚本&#xff0c;一个…

原神私服搭建教程(3.0本地版)

环境准备安装Java SE – 17 注意: 如果想仅运行服务端, 只下载 jre 即可MongoDB (推荐 4.0)代理程序: mitmproxy (仅需 mitmdump&#xff1b;推荐使用), Fiddler Classic 等校验win r &#xff0c;输入cmd 呼出控制台java&#xff1a;输入java -version 查看版本&#xff0c;正…

嵌入式Linux从入门到精通之第十二节:线程

线程类比于人得大脑,进程来实现具体操作 每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建、切换、撤销操作时,需要较大的系统开销。 为了减少系统开销,从进程中演化出了线程。 线程存在于进程中,共享进程的资源。 线程是进程中的独立控制流,由…

【C++】C++ 入门(三)

目录 一、内联函数 1、前置知识 2、内联函数概念 3、内联函数特性 4、补充内容 4.1、宏的优缺点 4.2、C有哪些技术替代宏 二、auto关键字(C11) 1、概念 2、使用场景 3、使用细则 3.1、auto与指针和引用结合起来使用 3.2、 在同一行定义多个变量 3.3、auto不能作为…

Java安全基础(二)Servlet核心技术

因为在实习中&#xff0c;文章更新速度可能有点慢&#xff0c;初学JAVA安全&#xff0c;内容如有不恰当的地方&#xff0c;欢迎各位大佬指正。 今天写一下Servlet的一些核心技术&#xff0c;后面更新完Filter之后我会对这两个进行一个总结。 了解Servlet知识对后续的框架审计…

自媒体人绝对要知道的6款软件工具!免费文案、配音不在话下

NO.1丨喵盐配音&#xff08;小程序&#xff09; 喵盐配音&#xff0c;它是我近期使用次数较多的配音小程序。这是一款专注于文字转语音的智能语音合成小程序&#xff0c;不需要下载&#xff0c;v小橙序搜索在线使用。其拥有200多个抖音热门发音人&#xff0c;支持普通话、英语、…

更安全的ftp服务器Pure-FTP搭建(4)

实验简介 实验所属系列&#xff1a;Linux服务器搭建 实验对象&#xff1a; 本科/专科信息安全专业 相关课程及专业&#xff1a;计算机基础&#xff0c;计算机网络 实验时数&#xff08;学分&#xff09;&#xff1a;2学时 实验类别&#xff1a;实践类预备知识 本实验要求实验者…

【JVM】Java类加载机制详解

【JVM】Java类加载机制详解 文章目录【JVM】Java类加载机制详解一&#xff1a;类加载子系统1&#xff1a;类加载器子系统的作用2&#xff1a;加载器 ClassLoader 的角色二&#xff1a;类的加载过程1&#xff1a;加载阶段2&#xff1a;验证阶段&#xff1a;确保被加载的类的正确…

[leetcode 215] 数组中的第K个最大元素

题目 题目&#xff1a;https://leetcode.cn/problems/kth-largest-element-in-an-array/description/ 解法 这道题目目前快排可以直接过&#xff0c;但是时间复杂度是 O(nlogn)O(nlogn)O(nlogn)。 想要 O(n)O(n)O(n)&#xff0c;这就涉及到408考研知识点了&#x1f602;&…

Java高手速成 | 使用TCP进行手机文件传输

由于TCP是面向流的&#xff0c;这意味着接收端有可能会在一次接收动作中接收两个或者多个数据包&#xff0c;那么当发送方需要把一个大文件分批连续发送时&#xff0c;如何保证接收方能够正确地接收并重修组会成一个完整的文件显得十分重要&#xff0c;本节通过一个端到端的手机…

每天10个前端小知识 【Day 4】

前端面试基础知识题 1. js中如何判断一个值是否是数组类型&#xff1f; instanceof const arr []; arr instanceof Array; // true Array.isArray const arr []; Array.isArray(arr) // true const obj {}; Array.isArray(obj) // false Object.prototype.isPrototype…