当前位置: 首页 > article >正文

golang 计时器内存泄露问题 与 pprof 性能分析工具

在这里插入图片描述
(上图用 go tool pprof 工具分析生成)
这种会造成内存泄露 因为每次for都会新建一个time对象,只有到期后会被回收。
解决方法:用time.NewTimer与time.Reset每次重新激活定时器

背景
我先贴一下会发生内存泄漏的代码段,根据代码可以更好的进行讲解:

func (b *BrokerImpl) broadcast(msg interface{}, subscribers []chan interface{}) {count := len(subscribers)concurrency := 1switch {case count > 1000:concurrency = 3case count > 100:concurrency = 2default:concurrency = 1}pub := func(start int) {for j := start; j < count; j += concurrency {select {case subscribers[j] <- msg:case <-time.After(time.Millisecond * 5):case <-b.exit:return}}}for i := 0; i < concurrency; i++ {go pub(i)}
}

看了这段代码,你知道是哪里发生内存泄漏了嘛?我先来告诉大家,这里time.After(time.Millisecond * 5)会发生内存泄漏,具体原因嘛别着急,我们一步步分析。

我们来写一段代码进行验证,先看代码吧:

package mainimport ("fmt""net/http"_ "net/http/pprof""time"
)/**time.After oom 验证demo*/
func main()  {ch := make(chan string,100)go func() {for  {ch <- "asong"}}()go func() {// 开启pprof,监听请求ip := "127.0.0.1:6060"if err := http.ListenAndServe(ip, nil); err != nil {fmt.Printf("start pprof failed on %s\n", ip)}}()for  {select {case <-ch:case <- time.After(time.Minute * 3):}}
}

这段代码我们该怎么验证呢?

看代码估计你们也猜到了,没错就是go tool pprof,可能有些小伙伴不知道这个工具,那我简单介绍一下基本使用,不做详细介绍,更多功能可自行学习。
再介绍pprof之前,我们其实还有一种方法,可以测试此段代码是否发生了内存泄漏,就是使用top命令查看该进程占用cpu情况,输入top命令,我们会看到cpu一直在飙升,这种方法可以确定发生内存泄漏,但是不能确定发生问题的代码在哪部分,所以最好还是使用pprof工具进行分析,他可以确定具体出现问题的代码。

proof 介绍
定位goroutine泄露会使用到pprof,pprof是Go的性能工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。使用pprof有多种方式,Go已经现成封装好了1个:net/http/pprof,使用简单的几行命令,就可以开启pprof,记录运行信息,并且提供了Web服务,能够通过浏览器和命令行2种方式获取运行数据。
基本使用也很简单,看这段代码:

package mainimport ("fmt""net/http"_ "net/http/pprof"
)func main() {// 开启pprof,监听请求ip := "127.0.0.1:6060"if err := http.ListenAndServe(ip, nil); err != nil {fmt.Printf("start pprof failed on %s\n", ip)}
}

使用还是很简单的吧,这样我们就开启了go tool pprof。下面我们开始实践来说明pprof的使用。
验证流程
首先我们先运行我的测试代码,然后打开我们的终端输入如下命令:

$ go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap

复制代码
浏览器会自动弹出,看下图:

看这个图,都爆红了,time.Timer导致占用CPU内存飙升,现在找到问题了,下面我们就可以来分析一下了。
原因分析
分析具体原因之前,我们先来了解一下go中两个定时器ticker和timer,因为不知道这两个的使用,确实不知道具体原因。

ticker和timer
Golang中time包有两个定时器,分别为ticker 和 timer。两者都可以实现定时功能,但各自都有自己的使用场景。
我们来看一下他们的区别:

ticker定时器表示每隔一段时间就执行一次,一般可执行多次。
timer定时器表示在一段时间后执行,默认情况下只执行一次,如果想再次执行的话,每次都需要调用 time.Reset()方法,此时效果类似ticker定时器。同时也可以调用stop()方法取消定时器
timer定时器比ticker定时器多一个Reset()方法,两者都有Stop()方法,表示停止定时器,底层都调用了stopTimer()函数。

原因
上面我们了介绍go的两个定时器,现在我们回到我们的问题,我们的代码使用time.After来做超时控制,time.After其实内部调用的就是timer定时器,根据timer定时器的特点,具体原因就很明显了。
这里我们的定时时间设置的是3分钟, 在for循环每次select的时候,都会实例化一个一个新的定时器。该定时器在3分钟后,才会被激活,但是激活后已经跟select无引用关系,被gc给清理掉。这里最关键的一点是在计时器触发之前,垃圾收集器不会回收 Timer,换句话说,被遗弃的time.After定时任务还是在时间堆里面,定时任务未到期之前,是不会被gc清理的,所以这就是会造成内存泄漏的原因。每次循环实例化的新定时器对象需要3分钟才会可能被GC清理掉,如果我们把上面代码中的3分钟改小点,会有所改善,但是仍存在风险,下面我们就使用正确的方法来修复这个bug。
修复bug
使用timer定时器
time.After虽然调用的是timer定时器,但是他没有使用time.Reset() 方法再次激活定时器,所以每一次都是新创建的实例,才会造成的内存泄漏,我们添加上time.Reset每次重新激活定时器,即可完成解决问题。

func (b *BrokerImpl) broadcast(msg interface{}, subscribers []chan interface{}) {count := len(subscribers)concurrency := 1switch {case count > 1000:concurrency = 3case count > 100:concurrency = 2default:concurrency = 1}//采用Timer 而不是使用time.After 原因:time.After会产生内存泄漏 在计时器触发之前,垃圾回收器不会回收Timerpub := func(start int) {idleDuration := 5 * time.MillisecondidleTimeout := time.NewTimer(idleDuration)defer idleTimeout.Stop()for j := start; j < count; j += concurrency {if !idleTimeout.Stop(){select {case <- idleTimeout.C:default:}}idleTimeout.Reset(idleDuration)select {case subscribers[j] <- msg:case <-idleTimeout.C:case <-b.exit:return}}}for i := 0; i < concurrency; i++ {go pub(i)}
}

总结

不知道这篇文章你们看懂了吗?没看懂的可以下载测试代码,自己测试一下,更能加深印象的呦~~~
这篇文章主要介绍了排查问题的思路,go tool pprof这个工具很重要,遇到性能和内存gc问题,都可以使用golang tool pprof来排查分析问题。不会的小伙伴还是要学起来的呀~~~
最后感谢指出问题的那位网友,让我又有所收获,非常感谢,所以说嘛,还是要共同进步的呀,你不会的,并不代表别人不会,虚心使人进步嘛,加油各位小伙伴们~~~

作者:Golang梦工厂
链接:https://juejin.cn/post/6874561727145443335
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

golang 计时器内存泄露问题 与 pprof 性能分析工具

&#xff08;上图用 go tool pprof 工具分析生成&#xff09; 这种会造成内存泄露 因为每次for都会新建一个time对象&#xff0c;只有到期后会被回收。 解决方法&#xff1a;用time.NewTimer与time.Reset每次重新激活定时器 背景 我先贴一下会发生内存泄漏的代码段&#xff0c…...

C#调用C++动态库时出现`System.DllNotFoundException`错误的解决思路

文章目录 1. DLL文件路径问题2. 依赖的运行时库缺失3. 平台不匹配&#xff08;x86/x64&#xff09;4. 导出函数名称不匹配5. DLL文件损坏或权限问题6. 运行时库冲突&#xff08;MT/MD不匹配&#xff09;7. 使用DLLImport时的常见错误总结步骤 在C#中调用C动态库时出现System.Dl…...

深度学习的下一个突破:从图像识别到情境理解

引言 过去十年&#xff0c;深度学习在图像识别领域取得了惊人的突破。从2012年ImageNet大赛上的AlexNet&#xff0c;到后来的ResNet、EfficientNet&#xff0c;再到近年来Transformer架构的崛起&#xff0c;AI已经能在许多任务上超越人类&#xff0c;比如人脸识别、目标检测、医…...

oracle查询是否锁表了

--查看当前数据库中被锁定的表数量 SELECT COUNT(*) FROM v$locked_object; select * from v$locked_object; --查看具体被锁定的表 SELECT b.owner, b.object_name, a.session_id, a.locked_mode FROM v$locked_object a, dba_objects b WHERE b.object_id a.object_id…...

从Oracle和TiDB的HTAP说起

除了数据库行业其他技术群体很多不知道HTAP的 时至今日还是有很多人迷信Hadoop&#xff0c;觉得大数据就是Hadoop。这是不正确的。也难怪这样&#xff0c;很多人OLTP和OLAP也分不清&#xff0c;何况HTAP。 Oracle是垂直方向实现 TiDB是水平方向实现 我个人认为这是两种流派…...

深入解析Spring Boot自动装配:原理、设计与最佳实践

引言 Spring Boot作为现代Java开发中的一股清流&#xff0c;凭借其简洁、快速和高效的特性&#xff0c;迅速赢得了广大开发者的青睐。而在Spring Boot的众多特性中&#xff0c;自动装载&#xff08;Auto-configuration&#xff09;无疑是最为耀眼的明珠之一。本文将深入剖析Sp…...

初识数据结构——算法效率的“两面性”:时间与空间复杂度全解析

&#x1f4ca; 算法效率的“两面性”&#xff1a;时间与空间复杂度全解析 1️⃣ 如何衡量算法好坏&#xff1f; 举个栗子&#x1f330;&#xff1a;斐波那契数列的递归实现 public static long Fib(int N) {if(N < 3) return 1;return Fib(N-1) Fib(N-2); }问题&#xf…...

【USRP】srsRAN 开源 4G 软件无线电套件

srsRAN 是SRS开发的开源 4G 软件无线电套件。 srsRAN套件包括&#xff1a; srsUE - 具有原型 5G 功能的全栈 SDR 4G UE 应用程序srsENB - 全栈 SDR 4G eNodeB 应用程序srsEPC——具有 MME、HSS 和 S/P-GW 的轻量级 4G 核心网络实现 安装系统 Ubuntu 20.04 USRP B210 sudo …...

《从零搭建Vue3项目实战》(AI辅助搭建Vue3+ElemntPlus后台管理项目)零基础入门系列第二篇:项目创建和初始化

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 《从零搭建Vue3项目实战》&#xff08;AI辅助…...

简单线程池实现

线程池的概念 线程池内部可以预先去进行创建出一批线程&#xff0c;对于每一个线程&#xff0c;它都会周期性的进行我们的任务处理。 线程内部在维护一个任务队列&#xff0c;其中我们外部可以向任务队列里放任务&#xff0c;然后内部的线程从任务队列里取任务&#xff0c;如…...

CentOS7 安装 LLaMA-Factory

虚拟机尽量搞大 硬盘我配置了80G&#xff0c;内存20G 下载源码 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git 如果下载不了&#xff0c;可以进入github手动下载&#xff0c;然后在传入服务器。 也可以去码云搜索后下载 安装conda CentOS7安装conda…...

最新扣子(Coze)案例教程:最新抖音视频文案提取方法替代方案,音频视频提取文案插件制作,手把手教学,完全免费教程

&#x1f468;‍&#x1f4bb; 星球群同学反馈&#xff0c;扣子平台的视频提取插件已下架&#xff0c;很多智能体及工作流不能使用&#xff0c;斜杠君这里研究了一个替代方案分享给大家。 方案原理&#xff1a;无论是任何视频或音频转文案&#xff0c;我们提取的方式首先都是要…...

三防笔记本有什么用 | 三防笔记本有什么特别

在现代社会&#xff0c;随着科技的不断进步&#xff0c;笔记本电脑已经成为人们工作和生活的重要工具。然而&#xff0c;在一些特殊的工作环境和极端条件下&#xff0c;普通笔记本电脑往往难以满足需求。这时&#xff0c;三防笔记本以其独特的设计和卓越的性能&#xff0c;成为…...

硬盘分区格式之GPT(GUID Partition Table)笔记250406

硬盘分区格式之GPT&#xff08;GUID Partition Table&#xff09;笔记250406 GPT&#xff08;GUID Partition Table&#xff09;硬盘分区格式详解 GPT&#xff08;GUID Partition Table&#xff09;是替代传统 MBR 的现代分区方案&#xff0c;专为 UEFI&#xff08;统一可扩展固…...

adb检测不到原来的设备List of devices attached解决办法

进设备管理器-通用串行总线设备 卸载无法检测到的设备驱动 重新拔插数据线...

案例分享(七):实现Apache-sharding-proxy的监控

案例分享(七):实现Apache-sharding-proxy的监控 背景部署流程背景 因业务需求,实现Apache-sharding-proxy的监控(基于Apache-sharding-agent)。 部署流程 1.下载agent的包,选择与sharding版本一致,要不然无法启动sharding 2.点击5.3.0之后可以看到有sharding,proxy…...

docker 安装 awvs15

安装好 docker bash <(curl -sLk https://www.fahai.org/aDisk/Awvs/check.sh) xrsec/awvs:v15等待完成后访问即可 地址: https://server_ip:3443/#/login UserName: awvsawvs.lan PassWord: Awvsawvs.lan修改密码 docker ps -a //查看容器&#xff0c;找到相应id d…...

Kafka在Vue和Spring Boot中的使用实例

Kafka在Vue和Spring Boot中的使用实例 一、项目概述 本项目演示了如何在Vue前端和Spring Boot后端中集成Kafka&#xff0c;实现实时消息的发送和接收&#xff0c;以及数据的实时展示。 后端实现&#xff1a;springboot配置、kafka配置、消息模型和仓库、消息服务和消费者、we…...

JSON 是什么?通俗详解

**JSON 是什么&#xff1f;通俗详解** --- ### **1. 一句话总结** **JSON&#xff08;JavaScript Object Notation&#xff09;** 是一种轻量级的 **数据交换格式**&#xff0c;就像“数据的快递包装盒”&#xff0c;用来在不同系统之间 **传递和存储信息**&#xff0c;简单易…...

Flutter:Flutter SDK版本控制,fvm安装使用

1、首先已经安装了Dart&#xff0c;cmd中执行 dart pub global activate fvm2、windows配置系统环境变量 fvm --version3、查看本地已安装的 Flutter 版本 fvm releases4、验证当前使用的 Flutter 版本&#xff1a; fvm flutter --version5、切换到特定版本的 Flutter fvm use …...

碰一碰发视频源头开发技术服务商

碰一碰发视频系统 随着短视频平台的兴起&#xff0c;用户的创作与分享需求日益增长。而如何让视频分享更加便捷、有趣&#xff0c;则成为各大平台优化的重点方向之一。抖音作为国内领先的短视频平台&#xff0c;在2023年推出了“碰一碰”功能&#xff0c;通过近距离通信技术实…...

Oracle 23ai Vector Search 系列之4 VECTOR数据类型和基本操作

文章目录 Oracle 23ai Vector Search 系列之4 VECTOR数据类型和基本操作VECTOR 数据类型基本语法Vector 维度限制和向量大小向量存储格式&#xff08;DENSE vs SPARSE&#xff09;1. DENSE存储2. SPARSE存储3. 内部存储与空间计算 Oracle VECTOR数据类型的声明格式VECTOR基本操…...

Java面试38-Dubbo是如何动态感知服务下线的?

首先&#xff0c;Dubbo默认采用Zookeeper实现服务注册与服务发现&#xff0c;就是多个Dubbo服务之间的通信地址&#xff0c;是使用Zookeeper来维护的。在Zookeeper上&#xff0c;会采用树形结构的方式来维护Dubbo服务提供端的协议地址&#xff0c;Dubbo服务消费端会从Zookeeper…...

C++day8

思维导图 牛客练习 练习 #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory> using namespace std; class user{ public: …...

MySQL的进阶语法8(SQL优化——insert、主键、order by、group by、limit、count和update)

目录 一、插入数据 1.1 insert 1.2 大批量插入数据 二、主键优化 2.1 数据组织方式 2.2 页分裂 2.2.1 主键顺序插入效果 2.2.2 主键乱序插入效果 2.3 页合并 2.4 索引设计原则 三、order by优化 3.1 执行以下两条语句&#xff08;无索引&#xff09; 3.2 创建索引…...

STM32 基础2

STM32中断响应过程 1、中断源发出中断请求。 2、判断处理器是否允许中断&#xff0c;以及该中断源是否被屏蔽。 3、中断优先级排队。 4、处理器暂停当前程序&#xff0c;保护断点地址和处理器的当前状态&#xff0c;根据中断类型号&#xff0c;查找中断向量表&#xff0c;转到…...

前端单页应用性能优化全指南:从加载提速到极致体验

一、SPA性能瓶颈深度剖析 1.1 核心性能指标解读 指标健康阈值测量工具优化方向FCP (首次内容渲染)< 1.8sLighthouse资源加载优化TTI (可交互时间)< 3.5sWebPageTestJavaScript优化LCP (最大内容渲染)< 2.5sChrome DevTools渲染性能优化CLS (布局偏移)< 0.1PageSp…...

自然语言处理利器NLTK:从入门到核心功能解析

文章目录 一、NLP领域的基石工具包二、NLTK核心模块全景解析1 数据获取与预处理2 语言特征发现3 语义与推理 三、设计哲学与架构优势1 四维设计原则2 性能优化策略 四、典型应用场景1 学术研究2 工业实践 五、生态系统与未来演进 一、NLP领域的基石工具包 自然语言工具包&…...

简述Unity对多线程的支持限制和注意事项

Unity是一个以单线程为核心设计的游戏引擎&#xff0c;其主线程负责渲染、物理模拟、脚本更新&#xff08;如Update和FixedUpdate&#xff09;等核心功能。虽然Unity允许开发者使用C#的多线程功能&#xff08;如System.Threading命名空间&#xff09;来创建和管理线程&#xff…...

Mysql 中有哪些日志结构?

在 MySQL 中&#xff0c;日志文件是非常重要的&#xff0c;它们用于记录数据库的各类活动&#xff0c;帮助管理员进行监控、调试、恢复、以及优化数据库性能。MySQL 提供了几种类型的日志&#xff0c;每种日志都有其特定的用途。以下是 MySQL 中常见的几种日志类型&#xff1a;…...