golang内存泄漏
golang也用了好几年了,趁着有空 整理归纳下,以后忘了好看下
一般认为 Go 10次内存泄漏,8次goroutine泄漏,1次是真正内存泄漏,还有1次是cgo导致的内存泄漏
1:环境
go1.20
win10
2:goroutine泄漏
单个Goroutine占用内存,可参考Golang计算单个Goroutine占用内存, 在不发生栈扩张情况下, 新版本Go大概单个goroutine 占用2.6k左右的内存
Goroutine 泄露的常见原因
1>. 从 channel 里读,但是同时没有写入操作
2> 向 无缓冲 channel 里写,但是同时没有读操作
3> 向已满的 有缓冲 channel 里写,但是同时没有读操作
4> select操作在所有的case上都阻塞
5> goroutine进入死循环或死锁,一直结束不了
处理
<1><2><3> 少撒加撒,没什么解释的
<4> 查看 case 阻塞 原因 有没有缓冲什么的,为什么都阻塞,有没有超时机制
<5> 为什么死循环或死锁
来个demo
package mainimport ("fmt""net/http"_ "net/http/pprof""sync/atomic""time"
)func pprofServer() {ip := "0.0.0.0:6060"if err := http.ListenAndServe(ip, nil); err != nil {fmt.Printf("start pprof failed on %s\n", ip)}
}
// 所有chan 阻塞
func goroutineblock() {ch1 := make(chan string) // 无缓冲channelch2 := make(chan string) // 无缓冲channelgo func() {select {case <-ch1:fmt.Println("output1")case <-ch2:fmt.Println("output2")}}()
}

补充下 pprof
通过 http://localhost:6060/debug/pprof/CMD 获取对应的采样数据。支持的 CMD 有:
goroutine: 获取程序当前所有 goroutine 的堆栈信息。
heap: 包含每个 goroutine 分配大小,分配堆栈等。每分配 runtime.MemProfileRate(默认为512K) 个字节进行一次数据采样。
threadcreate: 获取导致创建 OS 线程的 goroutine 堆栈
block: 获取导致阻塞的 goroutine 堆栈(如 channel, mutex 等),使用前需要先调用 runtime.SetBlockProfileRate
mutex: 获取导致 mutex 争用的 goroutine 堆栈,使用前需要先调用 runtime.SetMutexProfileFraction
GC的触发场景

0:gcTriggerHeap 程序检测到距上次 GC 内存分配增长超过一定比例时(默认 100%)触发,就是内存翻倍就GC
heapLive 表示当前堆中存活(正在使用)的对象的总大小。
它反映了程序当前实际使用的堆内存量。
随着程序分配新对象和释放旧对象,这个值会动态变化
gcPercent 是一个控制GC触发频率的参数。
默认值是100,表示当堆内存增长到上次GC后的2倍时触发新的GC。
可以通过环境变量 GOGC 或运行时函数 debug.SetGCPercent() 来调整。
1:gcTriggerTime 从上次GC后间隔时间达到了runtime.forcegcperiod 时间
// This is a variable for testing purposes. It normally doesn’t change.
var forcegcperiod int64 = 2 * 60 * 1e9
2:gcTriggerCycle 用户主动调用runtime.GC().
GoV1.8 三色标记法+混合写屏障法
参考 https://zhuanlan.zhihu.com/p/14541819173
垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的对象,让出存储器资源,无需程序员手动执行。
Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一定时间的STW(stop the world),STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,Golang进行了多次的迭代优化来解决这个问题。
三色并发标记法
三色标记法 实际上就是通过三个阶段的标记来确定清楚的对象都有哪些.
1> 就是只要是新创建的对象,默认的颜色都是标记为“白色”.
2> 每次GC回收开始, 然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入“灰色”集合。
3> 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
4> 重复第三步, 直到灰色中无任何对象.
5> 回收所有的白色标记表的对象. 也就是回收垃圾.
可以看出,在三色标记法中,导致对象丢失的有两个条件:
1> 一个白色对象被黑色对象引用**(白色被挂在黑色下)**
2> 灰色对象与它之间的可达关系的白色对象遭到破坏**(灰色同时丢了该白色)**
关于 stw
Go的STW持续时间
Go的垃圾回收器通过使用并发标记和后台并发清除来尽量减少STW的时间。这意味着在大多数情况下,Go程序不会因为垃圾回收而完全停止。然而,在某些情况下,比如在高负载或大量内存分配时,Go的垃圾回收器可能会触发一个较长的STW暂停。
较短的STW:在正常情况下,特别是在使用了Go 1.3及以后版本的程序中,STW暂停通常很短,可能只有几毫秒。
较长的STW:在一些极端情况下,如果内存分配非常快或者堆的大小增长非常快,可能会触发一个较长的STW暂停。这通常发生在堆的增长超过了预设的阈值,并且系统需要一次性清理大量对象时。
如何管理和减少STW时间
优化内存使用:通过减少内存分配和优化数据结构的使用,可以降低垃圾回收的频率和STW的必要性。
调整GC参数:Go提供了多个GC调优参数(例如GOGC),可以用来调整垃圾回收的行为。例如,增加GOGC的值可以减少垃圾回收的频率,但可能会增加STW的持续时间。
使用runtime.ReadMemStats监控内存使用:通过监控内存使用情况,可以更好地理解何时会发生垃圾回收,并据此优化代码。
在补充下 Golang中协程调度器
参考 https://blog.csdn.net/tiancityycf/article/details/103857524
三个必知的核心元素。(G、M、P)
G:Goroutine的缩写,一个G代表了对一段需要被执行的Go语言代码的封装
M:Machine的缩写,一个M代表了一个内核线程,等同于系统线程
P:Processor的缩写,一个P代表了M所需的上下文环境
G需要绑定在M上才能运行;
M需要绑定P才能运行;
上所述,一个G的执行需要M和P的支持。一个M在于一个P关联之后就形成一个有效的G运行环境 【内核线程 + 上下文环境】。每个P都含有一个 可运行G的队列【runq】。队列中的G会被一次传递给本地P关联的M并且获得运行时机。
M 与 P 总是一对一,P 与 G 总是 一对多, 而 一个 G 最终由 一个 M 来负责运行。
简单的来说,一个G的执行需要M和P的支持。一个M在与一个P关联之后形成了一个有效的G运行环境【内核线程 + 上下文环境】。每个P都会包含一个可运行的G的队列 (runq )。队列中的G会被一次传递给本地P关联的M并且获得运行时机。
M 与 P 总是一对一,P 与 G 总是 一对多, 而 一个 G 最终由 一个 M 来负责运行。
调度器的有两大思想:
复用线程:协程本身就是运行在一组线程之上,不需要频繁的创建、销毁线程,而是对线程的复用。在调度器中复用线程还有2个体现:1)work stealing,当本线程无可运行的G时,尝试从其他线程绑定的P偷取G,而不是销毁线程。2)hand off,当本线程因为G进行系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的线程执行。
利用并行:GOMAXPROCS设置P的数量,当GOMAXPROCS大于1时,就最多有GOMAXPROCS个线程处于运行状态,这些线程可能分布在多个CPU核上同时运行,使得并发利用并行。另外,GOMAXPROCS也限制了并发的程度,比如GOMAXPROCS = 核数/2,则最多利用了一半的CPU核进行并行。
调度器的两小策略:
抢占:在coroutine中要等待一个协程主动让出CPU才执行下一个协程,在Go中,一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死,这就是goroutine不同于coroutine的一个地方。
全局G队列:在新的调度器中依然有全局G队列,但功能已经被弱化了,当M执行work stealing从其他P偷不到G时,它可以从全局G队列获取G。
3:其他情况
1>slice、string 切片 误用造成内存泄漏 个人认为不应该叫泄漏 应该叫 浪费,就是你只需要吃一口饭就饱了,但你盛了一大碗饭
func main() {go pprofServer()time.Sleep(5 * time.Second)//for i := 0; i < 30; i++ {// goroutineblock()////}//test2s0 := sliceleak(getStringWithLengthOnHeap(1 << 20)) // 1M bytesprintln("finish") //第一次 调用 go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap time.Sleep(10 * time.Second)s0 = ""runtime.GC() //gcTriggerTime 等2分钟太久了,手动GC一次//第2次 调用 go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap select {}println("finish2", s0)
}
// 2切片 len(s1) >3
func sliceleak(s1 string) string {s0 := s1[:3]return s0
}func getStringWithLengthOnHeap(length int) string {if length < 0 {length = 0 // 处理负长度的情况,避免创建负长度的切片}bytes := make([]byte, length) // 创建一个指定长度的字节切片for i := range bytes { // 使用空格填充(或根据需要修改填充内容)bytes[i] = ' '}return string(bytes) // 将字节切片转换为字符串
}
一次在 println(“finish”) 后 time.Sleep(10 * time.Second) 前
第2次在等20秒后再调用的 控制再手动gc 后调用
切片浪费的内存也会释放,无非是 没释放前,浪费了,所以切片的 如果浪费很多,用重新分配后小的再copy过去,浪费不多,可以无视

2>time.After()的使用
func timeleak() {chs := make(chan int, 60)go func() {var num = 0for {num ++chs <- num }}()for true {select {case <-time.After(time.Second * 60): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())case num := <-chs:fmt.Printf("print:num %v\n", num )}}
//可以这么修改//delay := time.NewTimer(time.Second * 60)//defer delay.Stop()//for true {// delay.Reset(time.Second * 60)// select {// case <-delay.C:// fmt.Printf("time.After:%v", time.Now().Unix())// case v := <-chs:// fmt.Printf("print:%v\n", v)// }//}}
print:693435
print:693436
print:693437
print:693438
间隔 执行了2次

如改成
func timeleak() {chs := make(chan int, 100)go func() {var i = 0for {i++chs <- iif i%10 == 0 {time.Sleep(time.Millisecond)}}}()for true {select {case <-time.After(time.Second * 1000): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())case v := <-chs:if v%1000 == 0 {fmt.Printf("print:%v\n", v)}}}
}

从 10000内执行一次 第2次 大概是 40000-50000间
内存泄漏速度下降 了好多,泄漏的速度跟 case 执行速度又关
如果用default 如下,内存泄漏 更快,不用default time.After 会阻塞,用了,不阻塞了,死的更快
for loop 下的 select 中 default 需要慎用
func timeleak2() {var i int32 = 0for {select {case <-time.After(time.Second * 1000): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())default:i++if i < 50000 {fmt.Println("i=", i)}}}
}

3> 可以参考 https://blog.csdn.net/qq_38609643/article/details/144963265
这里就不一一 试了
(1)未及时释放的对象引用
(2)循环引用
(3)未关闭的资源(文件、网络连接等)
(4) 闭包引用外部变量
(5)使用了 sync.Pool 但没有清理
(6)不合理的 defer 使用
4>GC 频繁 排查 参考 https://zhuanlan.zhihu.com/p/18966775221
4: 生成svg
1:http://localhost:6060/debug/pprof/heap 生成heap文件
2:把heap 文件放到 执行文件同一目录
3:https://graphviz.org/download/ 下载 graphviz-12.2.1 (64-bit) ZIP archive [sha256] 配置 path ##路径不要有中文或其他标点符号 有时识别不了
3: go tool pprof heap
4:执行 svg 命令 生成 profile001.svg
5:浏览器打开
other 差异对比
差异对比 eg:
go tool pprof -base C:\Users\Administrator\pprof\pprof.testmemory.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.008.pb.gz C:\Users\Administrator\pprof\pprof.testmemory.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.009.pb.gz
5:如果觉得有用,麻烦点个赞,加个收藏
相关文章:
golang内存泄漏
golang也用了好几年了,趁着有空 整理归纳下,以后忘了好看下 一般认为 Go 10次内存泄漏,8次goroutine泄漏,1次是真正内存泄漏,还有1次是cgo导致的内存泄漏 1:环境 go1.20 win10 2:goroutine泄漏 单个Goroutine占用内存&…...
Redis存储⑩Redis的事务_弱化的原子性
目录 1. MySQL和Redis事务的区别 1.1 MySQL的事务 1.2 Redis的事务 2. Redis事务操作 2.1 MULTI multi 2.2 EXEC exec 2.3 DISCARD discard 2.4 WATCH 1. MySQL和Redis事务的区别 1.1 MySQL的事务 MySQL事务复习: MySQL数据库⑨_事务(四个属性…...
基于Flask的京东商品信息可视化分析系统的设计与实现
【Flask】基于Flask的京东商品信息可视化分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统能够灵活地执行SQL查询,提取出用于分析的关键数据指标。为了将这…...
QML ToolButton与DelayButton的使用、详解与自定义样式
QML MenuBarItem与MenuItem的使用、详解与自定义样式 一、介绍1、ToolButton常见用法基础示例设置图标 常用属性texticonenabledshortcutcheckable & checked 信号onClickedonPressed 和 onReleased 样式和外观使用场景 2、DelayButton使用场景核心属性1. delay 核心信号1.…...
数据结构:动态数组vector
vector 是 C 标准库的动态数组。 在C语言中一般初学者会使用malloc,int[n]等方式来创建静态数组,但是这种方式繁琐且容易出错。我们做算法题一般使用动态数组vector, 并且在刷题网站的题目给的输入一般也是vector类型。 示例:vect…...
JSON格式,C语言自己实现,以及直接调用库函数(一)
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。以下为你提供不同场景下常见的 JSON 格式示例。 1. 简单对象 JSON 对象是由键值对组成,用花括号 {} 包裹&…...
学习整理安装php的uuid扩展以及uuid调用方法
学习整理安装php的uuid扩展以及uuid调用方法 1、安装uuid依赖库2、下载并安装3、ini中添加扩展4、re2c版本报错5、uuid调用方法 1、安装uuid依赖库 yum -y install uuid uuid-devel e2fsprogs-devel libuuid-devel2、下载并安装 点我下载uuid安装包 wget http://pecl.php.ne…...
Elasticsearch实战应用:从“搜索小白”到“数据侦探”的进阶之路
引言:Elasticsearch——数据世界的“福尔摩斯” 大家好,今天我们要聊的是一个在数据世界中扮演“福尔摩斯”角色的工具——Elasticsearch。如果你曾经为海量数据的搜索和分析头疼不已,那Elasticsearch就是你的救星!它不仅能帮你快…...
Orange 单体架构 - 快速启动
1 后端服务 1.1 基础设施 组件说明版本MySQLMySQL数据库服务5.7/8JavaJava17redis-stackRedis向量数据库最新版本Node安装Node22.11.0 1.2 orange-dependencies-parent 项目Maven依赖版本管理 1.2.1 项目克隆 GitHub git clone https://github.com/hengzq/orange-depende…...
Spring Boot 入门 与 无法解析符号 springframework 的解决
Spring Boot 入门的关键步骤 1 创建 Maven 工程 操作目的: 通过 Maven 工程来管理项目依赖,Spring Boot 本身就依赖 Maven 或 Gradle 来简化依赖管理。 操作方法: 打开 IDEA(IntelliJ IDEA)。点击 New Project&#…...
3D模型在线转换工具:轻松实现3DM转OBJ
3D模型在线转换是一款功能强大的在线工具,支持多种3D模型格式的在线预览和互转。无论是工业设计、建筑设计,还是数字艺术领域,这款工具都能满足您的需求。 3DM与OBJ格式简介 3DM格式:3DM是一种广泛应用于三维建模的文件格式&…...
java中双亲委派详解
什么是双亲委派机制? 双亲委派机制(Parent Delegation Model)是Java类加载器(ClassLoader)加载类时的一种策略。它的核心思想是:当一个类加载器收到加载类的请求时,不会立即自己加载࿰…...
【Python爬虫(27)】探索数据可视化的魔法世界
【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取ÿ…...
网络安全-js安全知识点与XSS常用payloads
简介 JavaScript 是一种轻量级的编程语言,定义了HTML的行为。它与Java的关系类似周杰和周杰伦的关系(即没有关系)。 用法 HTML 中的脚本必须位于 <script> 与 </script> 标签之间。 脚本可被放置在 HTML 页面的 <body>…...
ip属地是电话号码吗怎么改
在数字化时代,IP属地作为网络身份的一部分,对于许多互联网用户来说并不陌生。然而,关于IP属地的具体含义以及如何更改它,却常常让一些用户感到困惑。特别是当提到IP属地与电话号码之间的关系时,更是容易让人产生误解。…...
Ubuntu中使用yum命令出现错误提示:Command ‘yum‘ not found
Ubuntu中使用yum命令出现以下错误提示: 解决方法如下 1、使用su或sudo -s命令使普通用户切换为root用户 2、然后检测是否安装了build-essential程序包,输入命令: apt-get install build-essential 3、进度走完后安装yum,输入命令: apt-get install yum 如果成功安装ÿ…...
【Vue.js 和 React.js 的主要区别是什么?】
Vue.js 和 React.js 的主要区别是什么? 前言 Vue.js 和 React.js 是当前最流行的两个前端框架,它们都用于构建用户界面,但在设计理念、语法和使用方式上有显著差异。本文将从多个维度对比 Vue.js 和 React.js 的主要区别,帮助开…...
HarmonyOS NEXT 全栈开发实战手册(API 12+)
一、HarmonyOS NEXT 架构升级与关键技术 1.1 分布式能力增强(重点标注) 跨设备计算网格:支持动态分配 GPU/CPU/NPU 资源 // 分布式渲染示例(API 12) import renderService from ohos.distributedHardware.render; l…...
【rt-thread】rt-thread 控制 led 的两种方式
1. pin设备 #define LED_PIN 3int led(void) {rt_uint8_t count;rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); for(count 0 ; count < 10 ;count){ rt_pin_write(LED_PIN, PIN_HIGH);rt_kprintf("led on, count : %d %d\r\n", count, rt_pin_read(LED_PIN));…...
Python爬虫系列教程之第十三篇:构建高可用爬虫系统 —— 混合架构与自动化监控
大家好,欢迎继续关注本系列爬虫教程!随着爬虫项目规模的不断扩大和业务需求的提升,单一技术方案往往难以满足实际应用中对高可用性、稳定性和自动化监控的要求。如何构建一个既能应对多种反爬策略,又能在异常情况下自动恢复、实时…...
picomax 中 rkipc 的main.c文件分析
main函数 int main(int argc, char **argv) {//wait_key_event 这个进程的pidpthread_t key_chk;LOG_DEBUG("main begin\n");//luckfox-pico/project/app/rkipc/rkipc/common/common.c中//显示版本rkipc_version_dump();//设置信号signal(SIGINT, sig_proc);signal(…...
深入理解 JVM 的栈帧结构
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
[oeasy]python068_异常处理之后做些什么_try语句的完全体_最终_finally
068_异常处理之后做些什么_finally 异常处理之后做些什么_try语句的完全体_最终_finally 回忆上次内容 我们了解了 try 的细节 except 可以 捕获到异常 但报错比较简单 添加图片注释,不超过 140 字(可选) 游乐场里面的 报错 更全 更丰富 …...
数据库端性能测试优化案例
数据库端性能测试优化案例 数据库是系统性能的关键瓶颈之一,特别是在高并发、大数据量的场景下。以下是一些典型的数据库端性能测试优化案例,涵盖了查询优化、索引策略、连接池管理、分布式数据库设计等多个方面: 1. 案例:优化慢…...
如何实现使用DeepSeek的CV模型对管道内模糊、低光照或水渍干扰的图像进行去噪、超分辨率重建。...
要使用 DeepSeek 的 CV 模型对管道内模糊、低光照或水渍干扰的图像进行去噪、超分辨率重建,一般可以按照以下步骤实现: 1. 准备工作 1.1 获取 API 访问权限 首先,你需要从 DeepSeek 官方获取 API 访问权限和相应的 API 密钥。这通常需要在 De…...
PySide6学习专栏(四):用多线程完成复杂计算任务
如果计程序中要处理一个非常庞大的数据集中的数据,且数据处理计算很复杂,造成数据处理占用大量时间和CPU资源,如果不用多线程,仅在主进程中来处理数据,将会使整个程序卡死,必须采用多线程来处理这些数据是唯…...
神经网络八股(1)
1.什么是有监督学习,无监督学习 有监督学习是带有标签的,无监督学习是没有标签的,简单来说就是有监督学习的输入输出都是固定的,已知的,无监督学习输入是已知的,输出是不固定的,无监督学习是通…...
深度学习每周学习总结Y1(Yolov5 调用官方权重进行检测 )
🍨 本文为🔗365天深度学习训练营 中的学习记录博客Y1中的内容 🍖 原作者:K同学啊 | 接辅导、项目定制 ** 注意该训练营出现故意不退押金,恶意揣测偷懒用假的结果冒充真实打卡记录,在提出能够拿到视频录像…...
计算机视觉基础|从 OpenCV 到频域分析
一、引言 在当今数字化时代,图像处理已渗透到我们生活的方方面面,从日常使用的智能手机拍照美化,到医学领域的精准诊断,再到自动驾驶中的环境感知,其重要性不言而喻。在图像处理领域中,OpenCV 和频域分析&…...
74. 搜索二维矩阵(LeetCode 热题 100)
题目来源; 74. 搜索二维矩阵 - 力扣(LeetCode) 题目内容: 给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &am…...
