社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近项目开发中遇到一个问题:在程序中大量使用golang slice导致内存占用暴涨。经过一番分析与定位最终解决了问题,把过程记录下来与大家分析。
因为是内存问题,所以首先使用golang的pprof包进行查看heap和alloc内存分配详情。
//在import中添加pprof包
_ "net/http/pprof"
//指定pprof对外提供的http服务的ip和端口,配置为0.0.0.0表示可以非本机访问
go func() {
http.ListenAndServe("0.0.0.0:9999", nil)
}()
netstat -antp|grep 9999
tcp6 0 0 :::9989 :::* LISTEN 28294/program
http://127.0.0.1:9999/debug/pprof/
go tool pprof http://127.0.0.1:9999/debug/pprof/heap
连接上后,可以使用top命令查看内存使用排行
然后,使用list命令直接可以查看到具体是哪一行分配了多少内存
由于pprof信息显示,大量内存都是由于在slice的append方法中分配的。于是又回顾了一下slice的原理和坑。
golang中slice是对数组的引用,底层实现实际上还是数组。对slice一定要谨慎使用append操作。如果cap未变化时,slice是对数组的引用,并且append会修改被引用数组的值。append操作导致cap变化后,会复制被引用的数组,然后切断引用关系。
经过分析和查资料发现,网上总结的容易导致内存不能及时回收的情况:
由于底层都是数组,如果截图长slice的一段,其实相当于引用了底层数组中的一小段。只要还有引用,golang的gc就不能回收数组。这种情况导致未使用的数组空间,未及时回收。
解决方案,新建一个长度为0的slice,将需要的一小段slice使用append方法添加到新的slice。再将原来的slice置为nil。
如果slice中包含很多元素,再只有一小部分元素需要使用的情况下。建议重新分配一个slice将需要保留的元素加入其中,将原来的长slice整个置为nil。
https://gfw.go101.org/article/memory-leaking.html
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!