golang内存泄漏

news2025/2/22 6:28:49

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 main

import (
	"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) // 无缓冲channel
	ch2 := make(chan string) // 无缓冲channel
	go 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()
	//
	//}
	//test2
	s0 := sliceleak(getStringWithLengthOnHeap(1 << 20)) // 1M bytes
	println("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 = 0
		for {
			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 = 0
		for {
			i++
			chs <- i
			if 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 = 0
	for {
		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:如果觉得有用,麻烦点个赞,加个收藏

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

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

相关文章

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事务复习&#xff1a; MySQL数据库⑨_事务&#xff08;四个属性…

基于Flask的京东商品信息可视化分析系统的设计与实现

【Flask】基于Flask的京东商品信息可视化分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统能够灵活地执行SQL查询&#xff0c;提取出用于分析的关键数据指标。为了将这…

QML ToolButton与DelayButton的使用、详解与自定义样式

QML MenuBarItem与MenuItem的使用、详解与自定义样式 一、介绍1、ToolButton常见用法基础示例设置图标 常用属性texticonenabledshortcutcheckable & checked 信号onClickedonPressed 和 onReleased 样式和外观使用场景 2、DelayButton使用场景核心属性1. delay 核心信号1.…

JSON格式,C语言自己实现,以及直接调用库函数(一)

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。以下为你提供不同场景下常见的 JSON 格式示例。 1. 简单对象 JSON 对象是由键值对组成&#xff0c;用花括号 {} 包裹&…

学习整理安装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…

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 工程 操作目的&#xff1a; 通过 Maven 工程来管理项目依赖&#xff0c;Spring Boot 本身就依赖 Maven 或 Gradle 来简化依赖管理。 操作方法&#xff1a; 打开 IDEA&#xff08;IntelliJ IDEA&#xff09;。点击 New Project&#…

3D模型在线转换工具:轻松实现3DM转OBJ

3D模型在线转换是一款功能强大的在线工具&#xff0c;支持多种3D模型格式的在线预览和互转。无论是工业设计、建筑设计&#xff0c;还是数字艺术领域&#xff0c;这款工具都能满足您的需求。 3DM与OBJ格式简介 3DM格式&#xff1a;3DM是一种广泛应用于三维建模的文件格式&…

网络安全-js安全知识点与XSS常用payloads

简介 JavaScript 是一种轻量级的编程语言&#xff0c;定义了HTML的行为。它与Java的关系类似周杰和周杰伦的关系&#xff08;即没有关系&#xff09;。 用法 HTML 中的脚本必须位于 <script> 与 </script> 标签之间。 脚本可被放置在 HTML 页面的 <body>…

ip属地是电话号码吗怎么改

在数字化时代&#xff0c;IP属地作为网络身份的一部分&#xff0c;对于许多互联网用户来说并不陌生。然而&#xff0c;关于IP属地的具体含义以及如何更改它&#xff0c;却常常让一些用户感到困惑。特别是当提到IP属地与电话号码之间的关系时&#xff0c;更是容易让人产生误解。…

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 如果成功安装&#xff…

深入理解 JVM 的栈帧结构

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

[oeasy]python068_异常处理之后做些什么_try语句的完全体_最终_finally

068_异常处理之后做些什么_finally 异常处理之后做些什么_try语句的完全体_最终_finally 回忆上次内容 我们了解了 try 的细节 except 可以 捕获到异常 但报错比较简单 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 游乐场里面的 报错 更全 更丰富 …

PySide6学习专栏(四):用多线程完成复杂计算任务

如果计程序中要处理一个非常庞大的数据集中的数据&#xff0c;且数据处理计算很复杂&#xff0c;造成数据处理占用大量时间和CPU资源&#xff0c;如果不用多线程&#xff0c;仅在主进程中来处理数据&#xff0c;将会使整个程序卡死&#xff0c;必须采用多线程来处理这些数据是唯…

神经网络八股(1)

1.什么是有监督学习&#xff0c;无监督学习 有监督学习是带有标签的&#xff0c;无监督学习是没有标签的&#xff0c;简单来说就是有监督学习的输入输出都是固定的&#xff0c;已知的&#xff0c;无监督学习输入是已知的&#xff0c;输出是不固定的&#xff0c;无监督学习是通…

深度学习每周学习总结Y1(Yolov5 调用官方权重进行检测 )

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客Y1中的内容 &#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 ** 注意该训练营出现故意不退押金&#xff0c;恶意揣测偷懒用假的结果冒充真实打卡记录&#xff0c;在提出能够拿到视频录像…

计算机视觉基础|从 OpenCV 到频域分析

一、引言 在当今数字化时代&#xff0c;图像处理已渗透到我们生活的方方面面&#xff0c;从日常使用的智能手机拍照美化&#xff0c;到医学领域的精准诊断&#xff0c;再到自动驾驶中的环境感知&#xff0c;其重要性不言而喻。在图像处理领域中&#xff0c;OpenCV 和频域分析&…

74. 搜索二维矩阵(LeetCode 热题 100)

题目来源; 74. 搜索二维矩阵 - 力扣&#xff08;LeetCode&#xff09; 题目内容&#xff1a; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &am…

netcore libreoffice word转pdf中文乱码

一、效果 解决&#xff1a; cd /usr/share/fonts/ mkdir zhFont cd zhFont #windows系统C:\Windows\Fonts 中复制/usr/share/fonts/zhFont sudo apt update sudo apt install xfonts-utils mkfontscale mkfontdir #刷新字体缓存 fc-cache -fv #查看已安装的字体列表 fc-list :…

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…