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 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:如果觉得有用,麻烦点个赞,加个收藏

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python Seaborn库使用指南:从入门到精通

1. 引言 Seaborn 是基于 Matplotlib 的高级数据可视化库,专为统计图表设计。它提供了更简洁的 API 和更美观的默认样式,能够轻松生成复杂的统计图表。Seaborn 在数据分析、机器学习和科学计算领域中被广泛使用。 本文将详细介绍 Seaborn 的基本概念、常用功能以及高级用法,…

修改与 Git 相关的邮箱

要修改与 Git 相关的邮箱信息&#xff0c;需要区分以下两种情况&#xff1a; 1. 修改 Git 提交时使用的邮箱&#xff08;影响提交记录&#xff09; Git 提交记录中的邮箱由本地 Git 配置的 user.email 决定&#xff0c;与 SSH 密钥无关。修改方法如下&#xff1a; 全局修改&a…

用PyTorch从零构建 DeepSeek R1:模型架构和分步训练详解

DeepSeek R1 的完整训练流程核心在于&#xff0c;在其基础模型 DeepSeek V3 之上&#xff0c;运用了多种强化学习策略。 本文将从一个可本地运行的基础模型起步&#xff0c;并参照其技术报告&#xff0c;完全从零开始构建 DeepSeek R1&#xff0c;理论结合实践&#xff0c;逐步…

基于SpringBoot的“流浪动物救助系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“流浪动物救助系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 局部E-R图 系统首页界面 系统…

从零开始玩转TensorFlow:小明的机器学习故事 5

图像识别的挑战 1 故事引入&#xff1a;小明的“图像识别”大赛 小明从学校里听说了一个有趣的比赛&#xff1a;“美食图像识别”。参赛者需要训练计算机&#xff0c;看一张食物照片&#xff08;例如披萨、苹果、汉堡等&#xff09;&#xff0c;就能猜出这是什么食物。听起来…

学习笔记--电磁兼容性EMC

一、基本概念 电磁兼容性&#xff08;Electromagnetic Compatibility&#xff0c;EMC&#xff09;是电子电气设备在特定电磁环境中正常工作的能力&#xff0c;同时不会对其他设备产生不可接受的电磁干扰。其核心目标是确保设备在共享的电磁环境中既能抵抗干扰&#xff0c;又能避…

unity学习51:所有UI的父物体:canvas画布

目录 1 下载资源 1.1 在window / Asset store下下载一套免费的UI资源 1.2 下载&#xff0c;导入import 1.3 导入后在 project / Asset下面可以看到 2 画布canvas&#xff0c;UI的父物体 2.1 创建canvas 2.1.1 画布的下面是 event system是UI相关的事件系统 2.2 canvas…

ArcGIS Pro中创建最低成本路径的详尽教程

一、引言 在地理信息系统&#xff08;GIS&#xff09;的应用场景中&#xff0c;路径分析扮演着至关重要的角色。而最低成本路径分析&#xff0c;则是路径分析中的一种高级应用&#xff0c;它综合考虑了地形、植被、土地利用类型等多种因素&#xff0c;通过加权计算得出一条从起…

地铁站内导航系统:基于蓝牙Beacon与AR技术的动态路径规划技术深度剖析

本文旨在分享一套地铁站内导航系统技术方案&#xff0c;通过蓝牙Beacon技术与AI算法的结合&#xff0c;解决传统导航定位不准确、路径规划不合理等问题&#xff0c;提升乘客出行体验&#xff0c;同时为地铁运营商提供数据支持与增值服务。 如需获取校地铁站内智能导航系统方案文…

在VSCode中接入deepseek

注册就送14元2000万tokens。 https://cloud.siliconflow.cn/i/rnbA6i6U各种大模型 下面介绍我是如如接入vscode的 左边生成一个key&#xff0c;呆会vscode要用&#xff0c;不然401. 打开vscod&#xff0c;电脑能上网。下插件。 下好要配置 点它一下。 要配置&#xff0c;全…

【Java项目】基于Spring Boot的简历系统

【Java项目】基于Spring Boot的简历系统 技术简介&#xff1a;采用Spring Boot框架、Java技术、MySQL数据库等实现。 系统简介&#xff1a;系统主要实现了管理员模块、用户模块二大部分。管理员登录进入简历系统可以查看首页、个人中心、用户管理、简历模板管理、模板类型管理、…

汽车零部件工厂如何通过ESD监控系统闸机提升产品质量

在汽车零部件工厂的生产过程中&#xff0c;静电带来的危害不容小觑。从精密的电子元件到复杂的机械部件&#xff0c;静电都可能成为影响产品质量的 “隐形杀手”。而 ESD 监控系统闸机的出现&#xff0c;为汽车零部件工厂解决静电问题、提升产品质量提供了关键的技术支持。 一、…

记录:Docker 安装记录

今天在安装 ollama 时发现无法指定安装目录&#xff0c;而且它的命令行反馈内容很像 docker &#xff0c;而且它下载的模型也是放在 C 盘&#xff0c;那么如果我 C 盘空间不足&#xff0c;就装不了 deepseek-r1:70b &#xff0c;于是想起来之前安装 Docker 的时候也遇到过类似问…

DPVS-5: 后端服务监控原理与测试

后端监控原理 被动监测 DPVS自带了被动监控&#xff0c;通过监控后端服务对外部请求的响应情况&#xff0c;判断服务器是否可用。 DPVS的被动监测&#xff0c;并不能获取后端服务器的详细情况&#xff0c;仅仅通过丢包/拒绝情况来发觉后端服务是否可用。 TCP session state…

第438场周赛:判断操作后字符串中的数字是否相等、提取至多 K 个元素的最大总和、判断操作后字符串中的数字是否相等 Ⅱ、正方形上的点之间的最大距离

Q1、判断操作后字符串中的数字是否相等 1、题目描述 给你一个由数字组成的字符串 s 。重复执行以下操作&#xff0c;直到字符串恰好包含 两个 数字&#xff1a; 从第一个数字开始&#xff0c;对于 s 中的每一对连续数字&#xff0c;计算这两个数字的和 模 10。用计算得到的新…

计算机毕业设计SpringBoot+Vue.js学科竞赛管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

2.24力扣-回溯电话号码的字母组合

17. 电话号码的字母组合 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<String> ans new LinkedList<>();StringBuilder temp new StringBuilder();public List<String> letterCombinations(String digits) {if(digitsnull || digits.leng…

在PyCharm中集成AI编程助手并嵌入本地部署的DeepSeek-R1模型:打造智能开发新体验

打造智能开发新体验&#xff1a;DeepSeekPycharmollamaCodeGPT 目录 打造智能开发新体验&#xff1a;DeepSeekPycharmollamaCodeGPT前言一、什么是ollama&#xff1f;二、如何使用1.进入ollama官方网站:2.点击下载ollama安装包3.根据默认选项进行安装4.安装成功5.打开命令提示符…

游戏引擎学习第120天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上次回顾&#xff1a;周期计数代码 我们正在进行一个项目的代码优化工作&#xff0c;目标是提高性能。当前正在优化某个特定的代码片段&#xff0c;已经将其执行周期减少到48个周期。为了实现这一目标&#xff0c;我们设计了一个…

三级等保|三级等保认证|三级等保申请

在信息化时代&#xff0c;信息安全已成为企业及组织不可缺的重要环节。三级等保作为我国信息安全等级保护体系中的重要组成部分&#xff0c;其办理过程需严格遵守相关法律法规和标准。以下是在办理三级等保过程中需要注意的若干事项&#xff1a; 一、明确等级保护对象 首先&…