前言:最近解决了一个项目中go的内存占用过高的问题,由于本人也是初学这门语言所以在问题排查和定位过程中也尝试了很多方法,同时也发现目前网上对于go这么语言的一些问题排查资料很少,所以整理一下自己在排查过程中的思路。
问题表象
最近发现服务被部署后很快会出现内存积压的问题,并且将内存打满。而这个服务使用的依旧是旧的镜像版本,所以可以排除新代码上线引入的问题。
发现问题后开始排查
俗话说,选好工具是成功的一半,所以我们先说一下go的内存分析工具
首先我们要说一下go的内存分析工具----pprof。具体工具使用可以看我的另一篇文章,在此不在赘述
工具使用介绍完毕,下面开始排查问题
首先通过命令查询内存分配
root@sns-ads-service06:~/pprof# go tool pprof -inuse_space http://localhost:6060/debug/pprof/heap
Fetching profile over HTTP from http://localhost:6060/debug/pprof/heap
Saved profile in /root/pprof/pprof.thriftcapture.alloc_objects.alloc_space.inuse_objects.inuse_space.003.pb.gz
File: thriftcapture
Build ID: 1da7c49a46e05f63089a7eecbe14129ad3948566
Type: inuse_space
Time: Apr 14, 2022 at 1:51am (UTC)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 9034.64kB, 100% of 9034.64kB total
Showing top 10 nodes out of 23
flat flat% sum% cum cum%
5120.23kB 56.67% 56.67% 5120.23kB 56.67% main.CopyMulty
2368.33kB 26.21% 82.89% 2368.33kB 26.21% github.com/buger/goreplay/raw_socket_listener.NewListener
1034.03kB 11.45% 94.33% 1034.03kB 11.45% runtime.procresize
512.05kB 5.67% 100% 512.05kB 5.67% os.newFile
0 0% 100% 512.05kB 5.67% github.com/buger/goreplay/raw_socket_listener.(*Listener).readPcap.func1
0 0% 100% 512.05kB 5.67% github.com/buger/goreplay/rlog.(*RLogger).GetMemoryUsageRate
0 0% 100% 512.05kB 5.67% github.com/buger/goreplay/rlog.(*logWriter).ReadFile
0 0% 100% 2368.33kB 26.21% main.(*RAWInput).listen
0 0% 100% 2368.33kB 26.21% main.InitPlugins
0 0% 100% 2368.33kB 26.21% main.NewRAWInput
(pprof) root@sns-ads-service06:~/pprof#
发现没有大的内存占用
然后换一是否是goroutine 泄漏,通过命令获取 goroutine信息
root@sns-ads-service06:/usr/bin# wget -O - http://localhost:6060/debug/pprof/goroutine?debug=1
--2022-04-16 03:50:10-- http://localhost:6060/debug/pprof/goroutine?debug=1
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:6060... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/plain]
Saving to: 'STDOUT'
goroutine 98 [chan receive, 8 minutes]:
github.com/buger/goreplay/rlog.(*logWriter).runCacheWrite(0xc00011c000)
/go/src/goreplay/rlog/writer.go:397 +0x7d
created by github.com/buger/goreplay/rlog.(*logWriter).initLogWriter
/go/src/goreplay/rlog/writer.go:172 +0x15b
goroutine 99 [chan receive, 8 minutes]:
github.com/buger/goreplay/raw_socket_listener.(*Listener).sendMsg(0xc0000eaea0)
/go/src/goreplay/raw_socket_listener/listener.go:176 +0x59
created by github.com/buger/goreplay/raw_socket_listener.NewListener
/go/src/goreplay/raw_socket_listener/listener.go:130 +0x2fa
goroutine 100 [chan receive, 8 minutes]:
github.com/buger/goreplay/raw_socket_listener.(*Listener).sendMsg(0xc0000eaea0)
/go/src/goreplay/raw_socket_listener/listener.go:176 +0x59
created by github.com/buger/goreplay/raw_socket_listener.NewListener
/go/src/goreplay/raw_socket_listener/listener.go:130 +0x2fa
发现进程执行也是正常的,因为代码中用到了一些chan,但并没有阻塞的情况
查询内存使用情况
$top
top - 01:56:27 up 36 days, 1:48, 0 users, load average: 11.97, 13.52, 22.38
Tasks: 5 total, 1 running, 4 sleeping, 0 stopped, 0 zombie
%Cpu(s): 16.0 us, 3.0 sy, 0.0 ni, 79.7 id, 0.0 wa, 0.0 hi, 1.4 si, 0.0 st
MiB Mem : 126289.6 total, 88208.2 free, 18361.2 used, 19720.2 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 107171.1 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6 root 20 0 8194872 2.0g 25584 S 101.3 1.7 6:47.79 server
1 root 20 0 3828 2848 2608 S 0.0 0.0 0:00.00 demo
72 root 20 0 4092 3408 2876 S 0.0 0.0 0:00.00 bash
104 root 20 0 4092 3312 2880 S 0.0 0.0 0:00.00 bash
110 root 20 0 6940 3240 2748 R 0.0 0.0 0:00.00 top
发现虽然内存分配不多,但是RES确实有很高的值、难道是因为RSS没有被回收
继续查找原因
(runtime: provide way to disable MADV_FREE · Issue #28466 · golang/go · GitHub)
如上issue所述,难道是GC策略的问题,感觉试一下
添加环境变量:GODEBUG=madvdontneed=1
然后提交代码,等待镜像打包,然后打包失败了。。。。
Step 4/16 : RUN git config --global url."git@code.devops.xiaohongshu.com:".insteadof "http://code.devops.xiaohongshu.com/" && go env -w GO111MODULE=on && go env -w GOSUMDB=off && go env -w GOPROXY=https://goproxy.cn,direct && go env -w GOPRIVATE=code.devops.xiaohongshu.com go env -w GODEBUG='madvdontneed=1'
---> Running in b74d9e5c4fa8
go env -w: arguments must be KEY=VALUE: invalid argument: go
基于我比较懒,所以想看看有没有别的办法
还真有
原来在Go1.16版本已经修复了这个问题
然后就是升级包版本,然后打包,构架镜像,部署,验证。
果然内存的问题有一定的缓解