本文摘自网络,作者,侵删。
go 是自带gc的语言,会自动管理内存,不用像C/C++那样,需要程序员手动释放内存,不用手动管理内存,就能少掉很多头发
go的GC会自动管理内存,但是这不代表go程序就不会内存泄露了。 go常见产生内存泄露的原因就是goroutine没有结束,简单说就是goroutine 被阻塞了,这样就会导致goroutine引用的内存不被GC回收,也就导致了内存写了。
当然产生内存泄露的原因还有别的,只是暂时我还没有遇到。不管什么原因产生的内存泄露,最终都是因为异常的引用,导致该被回收的内存没有被gc 回收掉
起因
说起go内存泄露分析,还要从年前的一次程序压测说起。我用一个测试程序压测我们游戏的一些数据,大约开了3000个tcp连接到游戏。游戏数据没有问题,但是当测试结束后,发现游戏的Gateway内存占用一直没有下降。本能的让我想起了是不是内存泄露了。马上用pprof分析了一下内存,发现果然是内存泄露了。
因为时公司代码,不方便拿出了分析,大体说一下原因吧
Gateway是一个读写分离的tcp服务,也就是每一个连接都要有两个goroutine,一个读,一个写。
但是当tcp连接断开时,因为时序问题,导致goroutine阻塞了,一直没有结束,就是导致了相关联的内存没有释放。
因为时公司代码,(入职签了保密协议,虽然也是一个屎山,但是不能随便贴出来,避免律师函警告),这次就自己写个简单的demo模拟一下吧
因为go 自带的pprof 只能展示问文字,不太明显,所有先安装一个可视化插件 graphviz 传送门</br>
linux上可以直接通过 apt 或者 yum 安装就行了。</br>
Windows/">windows上去网站下一个就好了,我下载 .msi 格式的安装后不能用,重新下了一个 压缩包,解压后把 bin 目录配置到环境变量的 path 中就可以使用了
开始
写一个简单的demo 模拟一下内存泄露
package main
import (
"math/rand"
"net/http"
_ "net/http/pprof"
"sync"
)
func main() {
go func() {
http.ListenAndServe("0.0.0.0:8090", nil)
}()
c := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
for i := 0; i < 10000; i++ {
go one(c)
}
wg.Wait()
}
func one(c chan struct{}) {
var a []int64
for i := 0; i < 10000; i++ {
a = append(a, rand.Int63())
}
<-c
}
程序很简单,在 for 循环中开启 1000 个协程,每个协程中往切片中append 1000个数据。</br>
用一个channel模拟协程阻塞,这样就会导致goroutine不会结束。
使用go再带的pprof查看
代码中监听了 8090 端口,在浏览器中输入 http://127.0.0.1:8090/debug/pprof/
下面都有解释,就不用详细介绍了,挑一两个说一下
allocs : 程序运行期间,所有内存分配的样本
heap : 当前活动对象的内存分配采样
guroutine : 当前所有协程的堆栈跟踪
可以看到,当前的程序总共有10005个协程,21次堆内存分配,说明我们的协程是被阻塞的。
使用graphviz查看
在命令行中,在Windows中可以使用 powerShell </br>
输入 go tool pprof -http :8081 http://127.0.0.1:8090/debug/pprof/heap
会在浏览器中打开
这就是当前 heap 采样图</br>
可以在 VIEW 中切换不同的显示方式
相关阅读 >>
手撸Golang Go与微服务 chatserver之3 压测与诊断
更多相关阅读请进入《Go》频道 >>
Go语言101
一个与时俱进的Go编程知识库。