GoLang内存泄漏原因排查详解

 更新时间:2022年12月15日 16:38:06   作者:上后左爱  
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,简单点说就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出出现out of memory异常

背景

Go 语言中有对应的Go 内存回收机制,在Go采用 并发三色标记清除  算法, 但是由于实际的过程中 发现会有一些内存泄漏的常见,内存泄漏 分为: 临时性 和 永久性内存泄漏。

初步排查过程中: 发现Linux使用top 发现内存随着时间会持续的增加没有稳定在一个合理值中。

在使用 pprof ,BBC 等 Go的内存泄漏工具进行排查

临时性内存泄漏

指的释放内存 不及时,对应的内存在更晚时候释放,这类问题主要是 string,slice 和底层的Buffer 错误共享,或者defer 函数资源没有及时的释放。

数组不正常使用导致内存泄漏

数组作为形参 遵守的是 值拷贝,如果函数调用次数很多并且数组过大,导致 内存使用激增

func countTragetBigNum(nums [1000000]int, target int) int {
   num := 0
   for i :=0; i< len(nums) && nums[i] ==target ; i++ {
   	num += 1
   }
   return num
}
# 在短时间内 创建100万数组 内存是8M,短时间内调用 100 次,并且作为是形参 在Go进行是 值Copy ,800M, 所以在时间过程中:

大数组在形参的场景下,通常使用的是切片方式。或者使用指针进行传递,避免在短时间内内存激增

goroutine 没有及时释放

一个 goroutine 创建 2KB

1. 互斥锁没有释放
func mutexTest() {
	mutex := sync.Mutex{}
	for i := 0; i < 10; i++ {
		go func () {
			mutex.Lock()
			fmt.Printf("%d goroutine get mutex", i)
			time.Sleep(100 * time.Microsecond)
		}()
	}
}
2. 死锁情形
3. 空channel --声明空channel但是没有初始化 导致读写被阻塞
func channelTest(){
	// 声明空 channel且不初始化导致阻塞
	var c chan int
	// 向通道中写数据
	go func() {
		c <- 1
		fmt.Printf("g1 send message")
	}()
	// 从通道中读数据
	go func(){
		<- c
		fmt.Printf("g2 receive message")
	}()
}
4. 能进不能出, 能出不能进 (写和读 通道量不一致)
导致大量的协程被阻塞
func channelTest() {
	// 声明空 channel且不初始化导致阻塞
	var c chan int
	// 10 个向通道中写数据,导致 写大于读量
	// 10个通道读数据,1个通道写数据也是一样的
	for i := 0; i < 10; i++ {
		go func() {
			c <- i
			fmt.Printf("g1 send message")
		}()
	}
	// 从通道中读数据
	go func() {
		<-c
		fmt.Printf("g2 receive message")
	}()
}
5. Time 定时器使用 定时器没有 手动Stop 会一直占有内存
func tickerTest() {
	ticker := time.NewTicker(time.Second * 20)
	go func() {
		for t := range ticker.C {
			fmt.Printf("ticker trigger")
		}
	}()
	time.Sleep(time.Second * 20)
	ticker.Stop()
}

通道理解

通道 channel 属于Go语言中首次提出的 运用 goroutine 的进行数据交换的 容器;

  • 缓冲通道属于一个元素队列, 有先入先出原则, 属于线程安全
  • 如果通道满了, 发送goroutine 会阻塞直到 read goroutine 进行接收操作
  • 如果通道是空的, 执行接收goroutine 阻塞直到 write goroutine 在通道上发送数据;
  • 通道既不满也不空,所以通道的缓冲区将发送和接收 goroutine 进行解耦操作

如果程序直到 缓冲区的容量, 可以调用 内置的  cap函数 cap(ch) -- 缓冲区容量
len(ch) -- 缓冲区内 元素个数

goroutine 泄漏: 使用一个无缓冲通道,两个慢的 goroutine 卡住,发送的响应的结果没有其他的goroutine 接收 这个情况属于 goroutine 泄漏;

如果读速度比较快,缓冲区存是没有意义的

到此这篇关于GoLang内存泄漏原因排查详解的文章就介绍到这了,更多相关Go内存泄漏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go打包附件内容到执行文件的方法

    Go打包附件内容到执行文件的方法

    处于种种原因, 我们不希望这部分额外的内容以附件的形式出现, 有没有什么办法能够将附件内容直接打包进可执行文件中呢,下面小编给大家介绍下Go打包附件内容到执行文件的方法,感兴趣的朋友一起看看吧
    2023-03-03
  • 浅谈golang slice 切片原理

    浅谈golang slice 切片原理

    这篇文章主要介绍了浅谈golang slice 切片原理,详细的介绍了golang slice 切片的概念和原理,具有一定的参考价值,有兴趣的可以了解一下
    2017-11-11
  • Go如何实现Websocket服务以及代理

    Go如何实现Websocket服务以及代理

    这篇文章主要介绍了Go如何实现Websocket服务以及代理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • Golang内存泄露场景与定位方式的实现

    Golang内存泄露场景与定位方式的实现

    Golang有自动垃圾回收机制,但是仍然可能会出现内存泄漏的情况,本文主要介绍了Golang内存泄露场景与定位方式的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-04-04
  • Go语言实战之详细掌握正则表达式的应用与技巧

    Go语言实战之详细掌握正则表达式的应用与技巧

    正则表达式是一种从左到右与主题字符串匹配的模式,正则表达式用于替换字符串中的文本,验证表单,基于模式匹配从字符串中提取子字符串等等,这篇文章主要给大家介绍了关于Go语言实战之详细掌握正则表达式的应用与技巧,需要的朋友可以参考下
    2023-12-12
  • 详解如何热重启golang服务器

    详解如何热重启golang服务器

    这篇文章主要介绍了详解如何热重启golang服务器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • GO利用channel协调协程的实现

    GO利用channel协调协程的实现

    本文主要介绍了GO利用channel协调协程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • golang通用的grpc http基础开发框架使用快速入门

    golang通用的grpc http基础开发框架使用快速入门

    这篇文章主要为大家介绍了golang通用的grpc http基础开发框架使用快速入门详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Golang性能提升利器之SectionReader的用法详解

    Golang性能提升利器之SectionReader的用法详解

    本文将介绍 Go 语言中的 SectionReader,包括 SectionReader的基本使用方法、实现原理、使用注意事项,感兴趣的小伙伴可以了解一下
    2023-07-07
  • Linux系统下Go语言开发环境搭建

    Linux系统下Go语言开发环境搭建

    这篇文章主要介绍了Linux系统下Go开发环境搭建,需要的朋友可以参考下
    2022-04-04

最新评论