关于golang程序内存问题查询的记录

背景:golang程序启动时,出现大量申请内存的情况,并且内存申请到一定值的时候,稳定住,不在增长

现象:经过测试,程序启动是会将数据库内容加载到内存,但是数据库中的数据量和程序申请的内存数量不匹配。比如数据库的数据量只有2G,但是程序启动后,居然申请到了10G的内存。并且只有在线上的情况下,该现象才会复现,也就是说,有请求进来时才会出现该现象。

 

怀疑1:程序有内存泄露,原因是只有在处理请求的情况下,程序的内存才会暴涨。但是如果有内存泄露的话,内存应该会一直涨,并不会涨到一定的值后,稳定下来。这个怀疑应该不成立。

 

一:打算使用go pprof查看下内存状态,记录下使用go pprof的步骤:

步骤1:程序中包含ggprof的包,import (_ "net/http/pprof")然后将程序启动。

步骤2:执行go tool pprof -seconds 30 http://localhost:7550/debug/pprof/heap 命令,如果查看内存,将heap改成profile

如下图所示:

关于golang程序内存问题查询的记录

 

如果png报错过程中报错,则安装一个可视化的工具,命令为:

yum install graphviz

 

使用上述方法,最后没有看到明显内存泄露的地方,使用内存比较多的都是一些go内部库。于是打算使用火焰图看一下:

二:使用火焰图步骤:

使用火焰图需要安装go-torch工具,安装步骤如下:

步骤1:执行命令:go get github.com/uber/go-torch

如果报错:go install github.com/uber/go-torch: mkdir /mnt/go1.9/bin/go: not a directory

则执行下export GOBIN=当前目录,因为需要在当前目录生成下go-torch的可执行文件

步骤2:进入目录cd src/github.com/uber/go-torch/ 执行go install 会在当前目录下生成go-torch可执行文件

步骤3:安装可视化工具:执行:git clone https://github.com/brendangregg/FlameGraph.git

步骤3:cp FlameGraph/flamegraph.pl ./

步骤4:执行:./go-torch -u http://127.0.0.1:7550/debug/pprof/heap --colors mem  -f mem.svg

如果报错,则将go的可执行文件,放入PATH下面再执行上述命令:

INFO[16:12:22] Run pprof command: go tool pprof -raw -seconds 30 http://127.0.0.1:7550/debug/pprof/profile

FATAL[16:12:22] Failed: could not get raw output from pprof: pprof error: exec: "go": executable file not found in $PATH

STDERR:

此时会生成mem.svg文件,放入浏览器查看即可。如果有内存泄露,会看到一个矩形从上到下都很大。

使用了上述两个工具之后,依然没查出来问题在哪。看图都是正常的。

最后发现在程序启动时,设置了export GOGC=800该参数,将该参数注释掉,内存在启动时,不会暴涨。

大致了解到:go语言里面有垃圾回收机制,每隔几分钟会执行一次,执行垃圾回收的时候,程序会暂停处理,这也是具有垃圾回收语言都存在的一个问题,所以像这些具有垃圾回收机制的语言不能用来做一些高实时行的开发,比如导弹,自动驾驶等,还得使用C/C++语言,因为这些场景不可能容忍程序暂停,即便是ms级别的时间,也可能会出事故。

 

看一些网上回答,在go的最初版本,GC存在很大的问题,回收的速度很慢,大概要执行几百ms,但是随着go版本的升级,目前垃圾回收做的越来越快。具体的GC原理还不是很懂。

 

具体讲解GC的可以参考下:https://www.cnblogs.com/wangyu19900123/p/11608461.html

 

关于GC看到一篇文章上说的:

GOGC是Go语言用于垃圾回收性能微调,GOGC 变量是设置初始的垃圾收集百分比。 当新分配的数据与上一个收集之后剩余的实时数据的比率达到该百分比时,触发垃圾收集 。

这里有一个设置权衡:“ 如果你想减少在GC上花费的总时间,增加GOGC,但是内存必须足够大。 如果你的内存少,你只能用更频繁的GC时间以节省内存,那么降低GOGC值'。

使用Go进行大数据处理,使用很多goroutines作为管道进行数据处理,导致性能非常差,他们将GOGC设置为500以后,最大可能会使用70GB,每条记录处理性能延迟从300μs降低110μs,使用Go 1.8RC3替代Go 1.6,每条记录延迟又降低了 25μs到80μs。

 

但是还是没太明白,GC达到百分比是指比如我设置为800,则达到初始内存的八倍才会回收,如果不设置,会每隔一段时间触发回收,对性能产生影响。

 

看的一篇文章写的改参数的设置含义:

GOGC参数主要控制的是下一次gc开始的时候的内存使用量。

比如当前的程序使用了4M的对内存(这里说的是堆内存),即是说程序当前reachable的内存为4m,当程序占用的内存达到reachable*(1+GOGC/100)=8M的时候,gc就会被触发,开始进行相关的gc操作。

如何对GOGC的参数进行设置,要根据生产情况中的实际场景来定,比如GOGC参数提升,来减少GC的频率。