【深入理解 ByteBuf 之一】 release() 的必要性

引言

开个新坑 【深入理解 ByteBuf】 至于为什么,本篇就是原因
我大概会花一个较长的时间来剖析 Netty 对于 ByteBuf 的实现,对象池的设计,从分配到释放重用,希望可以借此学习理解对象池的设计思想,以及搞清楚,我们不 release ByteBuf 泄露的究竟是什么,
以文章形式作为记录,希望对之后的开发设计有所启发,如果本系列文章帮助到了你,不胜荣幸。

ByteBuf 不 release 造成的内存泄露

首先模拟内存泄露的场景,这里我写了几个接口先通过 /buffer 接口进行循环分配,/metric 接口可以查看当前 分配器 的一些状态参数

@Slf4j
@RequestMapping("/api/bytebuf")
@RestController
public class ByteBufTestController {private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));@RequestMapping("/bufferNoPool")public ResultVO bufferNoPool() {final Runnable runnable = new Runnable() {@Overridepublic void run() {try {while (true) {final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(1024 * 10);buffer.writeBytes(new byte[1024 * 10]);buffer.retain();}}catch (Exception e){log.error("run buffer error" ,e);}}};Thread thread = new Thread(runnable);thread.start();return ResultVO.successResult(PooledByteBufAllocator.DEFAULT.toString());}@RequestMapping("/buffer")public ResultVO buffer() {final Runnable runnable = new Runnable() {@Overridepublic void run() {try {while (true) {final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(1024 * 10);buffer.writeBytes(new byte[1024 * 10]);buffer.retain();}}catch (Exception e){log.error("run buffer error" ,e);}}};threadPoolExecutor.submit(runnable);threadPoolExecutor.submit(runnable);return ResultVO.successResult(PooledByteBufAllocator.DEFAULT.toString());}@RequestMapping("/bufferAndRelease")public ResultVO bufferAndRelease() {final Runnable runnable = new Runnable() {@Overridepublic void run() {try {while (true) {final ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(1024 * 10);buffer.writeBytes(new byte[1024 * 10]);buffer.release();}}catch (Exception e){log.error("run bufferAndRelease error" ,e);}}};threadPoolExecutor.submit(runnable);threadPoolExecutor.submit(runnable);return ResultVO.successResult(PooledByteBufAllocator.DEFAULT.toString());}@RequestMapping("/metric")public ResultVO metric() {final PooledByteBufAllocatorMetric metric = PooledByteBufAllocator.DEFAULT.metric();ResultMap resultMap = new ResultMap();resultMap.put("numDirectArenas", metric.numDirectArenas());resultMap.put("usedDirectMemory", metric.usedDirectMemory());resultMap.put("metric", metric.toString());return ResultVO.successResult(resultMap);}}

配置好你本地的启动项,为了观测明显我分配了 2G 的可用直接内存

-Xms2G
-Xmx2G
-XX:MaxDirectMemorySize=2G
-XX:ThreadStackSize=512
-XX:MaxMetaspaceSize=256M
-XX:MetaspaceSize=256M
-Dio.netty.leakDetection.level=paranoid
--add-opens
java.base/java.lang=ALL-UNNAMED
--add-opens
java.base/java.io=ALL-UNNAMED
--add-opens
java.base/java.math=ALL-UNNAMED
--add-opens
java.base/java.net=ALL-UNNAMED
--add-opens
java.base/java.nio=ALL-UNNAMED
--add-opens
java.base/java.security=ALL-UNNAMED
--add-opens
java.base/java.text=ALL-UNNAMED
--add-opens
java.base/java.time=ALL-UNNAMED
--add-opens
java.base/java.util=ALL-UNNAMED
--add-opens
java.base/JDK.internal.access=ALL-UNNAMED
--add-opens
java.base/JDK.internal.misc=ALL-UNNAMED
--add-opens
java.base/sun.net.util=ALL-UNNAMED

在这里插入图片描述

那项目刚启动可以观察到,在活动监视器中的内存占用是 1G 多

在这里插入图片描述

运行时可以尝试通过 JProfiler 来监听内存和 GC 情况,下面是正常运行的检测

在这里插入图片描述

在这里插入图片描述

请求分配并且不释放时,堆内存增长,经过 GC 后呈现尖刺状,最后趋近平稳是线程分配已经报错,无法进行分配了,可以看到整个 Java 程序占用 4G 多而且一直不会释放。

在这里插入图片描述

再次 GC 后回归正常

在这里插入图片描述

但是整个程序堆外内存已经无法分配了

在这里插入图片描述

Exception in thread "Thread-53" java.lang.OutOfMemoryError: Cannot reserve 4194304 bytes of direct buffer memory (allocated: 2146073230, limit: 2147483648)at java.base/java.nio.Bits.reserveMemory(Bits.java:178)at java.base/java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:121)at java.base/java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:332)at io.netty.buffer.PoolArena$DirectArena.allocateDirect(PoolArena.java:701)at io.netty.buffer.PoolArena$DirectArena.newChunk(PoolArena.java:676)at io.netty.buffer.PoolArena.allocateNormal(PoolArena.java:215)at io.netty.buffer.PoolArena.tcacheAllocateSmall(PoolArena.java:180)at io.netty.buffer.PoolArena.allocate(PoolArena.java:137)at io.netty.buffer.PoolArena.allocate(PoolArena.java:129)at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:400)at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:188)at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:179)at io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:116)

可以看到这里报错的限制值与配置的 2G 是一致的

在这里插入图片描述

那其实能看到明显的堆内存浮动是因为我代码中分配 ByteBuf 的时候同时 new 了一个 byte 数组,去掉这行代码同样可以观察到堆外内存一直居高不下,堆内存没有影响,只有一次明显的 GC 活动

 buffer.writeBytes(new byte[1024 * 10]);

在这里插入图片描述

这说明如果你没有正确的 release ByteBuf 会导致堆外内存无法释放,从而导致内存泄露,再次尝试申请会报 OOM 错误。

也就是说即使 JVM 帮你回收了没有引用的 ByteBuf,但是 ByteBuf 占用的堆外内存也不会得到释放

at java.base/java.nio.Bits.reserveMemory(Bits.java:178)

在这里插入图片描述

如果调用的是分配并正确释放方法,可以观察到内存的使用是稳定的,GC 来自于堆内引用的申请和释放

在这里插入图片描述

至此已经复现了问题,并认识到了其严重性,那么具体到代码里,究竟是什么没有释放呢?Netty 为什么没有相关容错的机制?

这个问题勾起了我的好奇心,而故事可能要从对象池的设计讲起

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

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

相关文章

XTU OJ 1525瓷片

题意 给定一个2n的地面&#xff0c;用11和1*2的瓷片铺满&#xff0c;问有多少种方案 数据范围 n<30 输入 3 1 2 30 输出 2 7 1084493574452273 代码 #include<stdio.h>int main() {int t;scanf("%d",&t);long long a[40];a[0]1,a[1]2,a[2]7;fo…

Vue 3.4 发布

本文为翻译 原文地址&#xff1a;Announcing Vue 3.4 | The Vue Point — Vue 3.4 发布公告 |Vue 点 (vuejs.org) 今天&#xff0c;我们很高兴地宣布 Vue 3.4 “&#x1f3c0;灌篮高手”的发布&#xff01; 此版本包括一些实质性的内部改进 - 最引人注目的是重写的模板解析器&…

晶圆代工降价竞争进入白热化,降幅最高15% | 百能云芯

随着半导体产业的不确定性和市况回落&#xff0c;晶圆代工市场再次掀起波澜&#xff0c;“降价大军”再添猛将。 据综合媒体报道&#xff0c;传三星计划在2024年第一季度调降晶圆代工报价&#xff0c;提供5%至15%的折扣&#xff0c;并表示愿意进一步协商。 台积电根据客户的投产…

一、Vue3组合式基础[ref、reactive]

一、ref 解释&#xff1a;ref是Vue3通过ES6的Proxy实现的响应式数据&#xff0c;其与基本的js类型不同&#xff0c;其为响应式数据&#xff0c;值得注意的是&#xff0c;reactive可以算是ref的子集&#xff0c;ref一般用来处理js的基本数据类型如整型、字符型等等(也可以用来处…

Java集合框架深度解析-ArrayList

Java的集合框架提供了一组实现常用数据结构的类和接口。理解集合框架对于Java程序员来说至关重要&#xff0c;因为它们在日常编程中广泛应用。 为什么需要集合框架&#xff1f; 在编程中&#xff0c;我们经常需要存储和操作一组对象。集合框架提供了用于表示和操作对象组的通…

从 YOLOv1 到 YOLO-NAS 的所有 YOLO 模型:论文解析

在计算机视觉的浩瀚领域&#xff0c;有一支耀眼的明星&#xff0c;她的名字传颂着革新与突破的传奇——YOLO&#xff08;You Only Look Once&#xff09;。回溯时光&#xff0c;走进这个引人注目的名字背后&#xff0c;我们仿佛穿越进一幅画卷&#xff0c;一幅展现创新魅力与技…

Unity之预制体与变体

PS:不用说了&#xff0c;我在写博客就是在摸鱼 一、预制体 不知道大家小时候有没有看过火影&#xff0c;记得剧情最开始的时候水木哄骗鸣人去偷封印之书&#xff0c;反而让鸣人学会了多重影分身之术&#xff1a; 好了&#xff0c;小编绞尽脑子终于想好怎么向大家介绍预制体了&a…

Java中包装类型使用‘==’比较的坑

1、缓冲机制 在Java中&#xff0c;Integer 、Byte 、Short 、Long 、Character 五大包装类都有缓冲机制&#xff0c; 且缓冲的默认值范围都是 -128 ~ 127 而Float 、Double 、Boolean 三大包装类并没有缓冲机制。 2、创建包装类对象 两种方法&#xff1a; &#xff08;1&am…

PageHelper 使用示例

1. mapper 层返回的 List 的泛型和 service 层返回的PageInfo 的泛型类型一致&#xff1a; Override public PageInfo<VO1> queryVO1List(String startTime,String endTime,Integer pageNum,Integer pageSize) {boolean pageStarted false;try {PageHelper.startPage( …

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理

经典目标检测YOLO系列(一)复现YOLOV1(4)VOC2007数据集的读取及预处理 之前&#xff0c;我们依据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;提出了新的YOLOV1架构&#xff0c;并解决前向推理过程中的两个问题&#xff0c;继续按照此书进行YOLOV1的复现。 经典目标检…

【漏洞复现】通天星CMSV6车载监控平台任意文件下载漏洞

Nx01 产品简介 深圳市通天星科技有限公司&#xff0c;是一家以从事计算机、通信和其他电子设备制造业为主的企业。通天星车载视频监控平台软件拥有多种语言版本。应用于公交车车载视频监控、校车车载视频监控、大巴车车载视频监控、物流车载监控、油品运输车载监控、警车车载视…

十七:爬虫-JS逆向(上)

1、什么是JS、JS反爬是什么&#xff1f;JS逆向是什么? JS:JS全称JavaScript是互联网上最流行的脚本语言&#xff0c;这门语言可用于HTML 和 web&#xff0c;更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。JavaScript 是一种轻量级的编程语言。JavaScript 是…

数据结构—图(上)

文章目录 12.图(上)(1).图的基本概念#1.图的基本定义#2.边的分类#3.数据结构的一些规定#4.子图#5.完全图#6.路径#7.连通性和连通分量#8.度 (2).图的存储方式#1.邻接矩阵#2.邻接表 (3).图的遍历#1.深度优先搜索(Depth First Search)i.走个迷宫ii.DFS的思想iii.代码实现 #2.广度优…

不可狗尾续貂的tail

文章目录 不可狗尾续貂的tail常规使用显示N行从第N行显示按照字符显示特别赞的一个实时更新的功能更多信息不可狗尾续貂的tail tail命令用来查看文件尾部的n行,如果没有指定的n,默认显示10行。 命令格式: $ tail [option] [filename] 参数option比较常用的如下所示: -f…

LeetCode-无重复字符的最长子串(3)

题目描述&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 代码&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {Set<Character> occnew HashSet<Character>();int lens.length();int…

解决Element-Plus中el-switch的change方法自动触发问题

下面el-switch代码片段是在el-table里使用 <el-switch v-else-if"col.prop status" v-model"scope.row.status" :active-value"0" :inactive-value"1" :before-change"beforeStatus" change"changStatus(scope.r…

Yolov5/8的小程序部署前后端实现

Yolov5/8的小程序部署前后端实现 导语本机配置硬件环境配置 前端实现后端实现总结参考文献 导语 毕设的题目与Yolo系列的图像识别相关&#xff0c;通过搜查了很多资料和实践最后完成&#xff0c;看到某些平台上居然卖300&#xff0c;觉得很离谱&#xff0c;所以决定把代码开源…

Springcloud alibab和dubbo有什么区别?

Spring Cloud Alibaba 和 Dubbo 都是为了简化企业级应用开发而生的框架&#xff0c;尤其是在分布式系统和微服务架构的背景下。 虽然他们在某些功能上有重叠&#xff0c;但各有侧重点和使用场景。 微服务架构图 首先介绍一下 Spring Cloud Alibaba&#xff1a; Spring Cloud …

C++自制小游戏《屠夫躲猫猫》

大家好&#xff0c;我是派蒙&#xff0c;我写了一个《屠夫躲猫猫》的游戏&#xff0c;下面是源代码&#xff1a; #include <stdio.h> #include <conio.h> #include<bits/stdc.h> #include<windows.h> using namespace std; string ID[1001]; string N…

Fiddler抓取https原理?

首先fiddler截获客户端浏览器发送给服务器的https请求&#xff0c; 此时还未建立握手。 第一步&#xff0c; fiddler向服务器发送请求进行握手&#xff0c; 获取到服务器的CA证书&#xff0c; 用根证书公钥进行解密&#xff0c; 验证服务器数据签名&#xff0c; 获取到服务器C…