内存泄漏1.1事发现场是晴天。看着需求,敲着代码,憧憬着美好的未来。突然收到内存占用过高的警告。1.2证人证言警告项目旧代码是python,最近已经退役了。随着gorate继续上升,发现内存的RSS使用率在飙升。当最终达到容器内存限制时,进程将自动重新启动。RSS如下图所示:2检查内存泄漏2.1问题分析看到RSS异常增长,第一反应是:是不是最近上传的代码有问题?是否存在内存泄漏?内存泄漏是个大问题,所以检查一下。因此,延长时间线以查看它何时开始。结果,现实是残酷的。这个问题从项目一开始就存在。由于项目是2周版本,之前没有达到内存限制,所以没有发出警告。那么问题应该出在原来的版本上。这时,我想到了。会不会是我们用的框架有问题?但是这个想法很快就被否决了,因为我们使用的框架是其他项目已经上线很久的成熟框架。应该不会有这个问题。显然,光看代码是不可能发现问题的。于是想到了pprof这个golang的性能分析工具。由于没有开启pprof线上环境,我这里只能查看预发布环境。2.2发现问题2.2.1获取内存占用监控gotoolpprof-source_path=/path/to/gopath-inuse_spacehttps://target.service.url/debug/pprof/allocs-source_path分析代码时源文件的搜索路径,需要使用源码路径,这里是自己本地的gopath路径/debug/pprof/allocs-inuse_space和-sample_index=inuse_space一样是监控内存使用情况。因为我们分析的是内存泄漏,所以我们需要查看的是实际占用的内存。输入上面的命令,会出现如下界面:2.2.2分析内存监控2.2.2.1获取top10的内存使用情况由于我们要分析内存使用情况,所以此时输入一个top10,看看哪些代码占用了内存前10名内存。(pprof)top10Showingnodesaccountingfor145.07MB,92.64%of156.59MBtotalDropped163nodes(cum<=0.78MB)Showingtop10nodesoutof157flat%sum%cumcum%117.36MB74.95%74.95%117.36MB74.95%github.com/beorn7/perks/quantile.newStream(内联)14.55MB9.29%84.25%134.42MB85.84%github.com/prometheus/client_golang/prometheus.newSummary3.53MB2.25%86.50%4.06MB2.59%压缩/压缩。NewWriter2MB1.28%87.77%2MB1.28%github.com/prometheus/client_golang/prometheus.MakeLabelPairs1.53MB0.97%88.75%1.53MB0.97%github.com/rcrowley/go-metrics.newExpDecaySampleHeap1.50MB0.96.71%89.76%81%go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).snapshot1.50MB0.96%90.67%1.50MB0.96%github.com/Shopify/sarama.(*TopicMetadata).decode1.06MB0.68%91.35%1.06MB0.68%github.com/valyala/fasthttp/stackless.NewFunc1.03MB0.66%92.00%1.03MB0.66%github.com/xdg-go/stringprep.init1MB0.64%92.64%1MB0.64%strings.(*Builder).WriteByte这时候我们需要解释一下显示的指标flat的含义:内存usageofthefunctionflat%:函数占用内存的百分比sum%:所有函数从上到下到当前行的累计内存使用率。比如第二行,sum=84.25=74.95+9.29cum:函数及子函数运行占用内存应大于等于flatcum%:本函数及其子函数占用内存的比例functionsshouldbegreaterthanorequaltoflat%好了,这个newStream问题大了,我们进去看看是哪一行代码有问题我们需要使用一些命令。让我们输入listgithub.com/beorn7/perks/quantile.newStream来查找(pprof)list:Outputannotatedsourceforfunctionsmatchingregexp显示具体调用的代码块,并显示对应的指标(pprof)listgithub。com/beorn7/perks/quantile.newStreamTotal:156.59MBROUTINE==========================github.com/beorn7/perks/quantile.newStream在pkg/mod/github.com/beorn7/perks@v1.0.1/quantile/stream.go117.36MB117.36MB(持平,累积)总计的74.95%。.128:排序布尔。.129:}。.130:。.131:funcnewStream(?不变)*Stream{..132:x:=&stream{?:?}117.36MB117.36MB133:return&Stream{x,make(Samples,0,500),true}..134:}..135:.136://Insert将v插入到流中。..137:func(s*Stream)Insert(vfloat64){..138:s.插入(样品le{Value:v,Width:1})2.2.2.3分析泄漏原因看到这里应该可以看出这个newStream的内存占用情况,主要是生成了一个容量为500的数组。这个数组是什么样子的??typeSamplestruct{Valuefloat64`json:",string"`Widthfloat64`json:",string"`Deltafloat64`json:",string"`}从上面的结构可以看出,生成一次需要的内存就是500*3*8字节,那么一次就是12000字节,也就是差不多11.72kb。从这个角度来看,应该是有一个地方一直在调用,导致数据不断膨胀。见状,我们继续往下追。
