解决golang内存溢出的方法
作者:liaoyizhe 发布时间:2024-05-21 10:20:12
最近在项目中出现golang内存溢出的问题,master刚开始运行时只有10多M,运行几天后,竟然达到了10多个G。而且到凌晨流量变少内存也没有明显降低,内存状态呈现一种很不健康的曲线。
像这种情况肯定是golang内存溢出了,为此我持续排查了两天,终于找到问题所在,特此记录下。
准备工作
一台较好的环境测试机,单台运行无污染。
压测工具,无论服务是http还是websocket服务,都必须准备好压测工具模拟最真实的用户场景。
将master引入net/http/pprof包,通过http访问获得goroutine、heap信息。
//引入pprof
import _"net/http/pprof"
//在main中加入
go func() {
log.Println(http.ListenAndServe("localhost:9999", nil))
}()
浏览器访问: http://127.0.0.1:9999/debug/pprof/
获取goroutine信息 http://10.13.132.91:9999/debug/pprof/goroutine?debug=2
获取heap信息 http://10.13.132.91:9999/debug/pprof/heap?debug=2
使用golang tool进行统计分析,go tool pprof -inuse_space http://127.0.0.1:9999/debug/pprof/heap
。输入top10可以看出前十占用内存情况,这里我是直接输入png导出图片来查看,以便以后比较。还有两个参数可以选择,-inuse_space顾名思义是正在使用的内存,-alloc_space是已经分配的内存,本次我是一直用-inuse_space进行分析。
开始进行分析
go是一门自己gc的语言,大概两分钟会gc一次。如果有内存泄漏,无非就是两种情况。
有goroutine泄漏,goroutine“飞”了,zombie goroutine没有结束,这个时候在这个goroutine上分配的内存对象将一直被这个僵尸goroutine引用着,进而导致gc无法回收这类对象,内存泄漏。
有一些全局(或者生命周期和程序本身运行周期一样长的)的数据结构意外的挂住了本该释放的对象,虽然goroutine已经退出了,但是这些对象并没有从这类数据结构中删除,导致对象一直被引用,无法被回收。
排除掉goroutine泄漏
首先,我利用压测工具对server进行100个websocket连接,模拟用户浏览行为,然后关闭连接。打开浏览器查看goroutine数量,发现新起的goroutine全部已经销毁,没有观察到有泄漏的goroutine,因此排除此情况。
确定是全局变量无回收
排除goroutine泄漏,只能是由全局状态变量引起的。再次用压测工具进行压测然后关闭,使用观察内存情况。使用go tool pprof -inuse_space http://127.0.0.1:9999/debug/pprof/heap
输入png
导出(在这种情况下,需要等程序gc完再导出,建议等10分钟左右。)
发现问题所在
每次都会遗留这么大概0.5M的内存空间出来,就奇怪,明明整个goroutine退出为什么还有会内存占用?相应的全局变量也会删除该地方的引用。等一下,全局变量,难道是删除的时候没做好配对导致没有真正删除该引用吗?去查了下代码,果然是没有删除引用导致的,至此问题解决。
这里面有个项目的坑,上报日志的key不是根据这个len(map)
计算出,导致上报日志的时候以为删除了该key。
后记
为什么会花了两天时间,看起来上述流程并不复杂。
实际上你要完全排除掉goroutine泄漏需要花较长的时间去对比的,查看哪些goroutine是新起来没有关闭。
在使用-inuse_space或者-alloc_space分析,也是很纠结,这些看起来也并不完全与表现对应上。实际上用-inuse_space是较为直观的,可以展现出程序真正在使用的(RSS)。Go 管理内存的方式可能与你以前使用的方式不太一样。它会在一开始就保留一大块 VIRT,而 RSS 与实际内存用量接近。RSS 和 VIRT 之间有什么区别呢?VIRT 或者虚拟地址空间大小是程序映射并可以访问的内存数量。RSS 或者常驻大小是实际使用的内存数量。因此用-inuse_space导出在png图上的统计中,与top上的res值是大致相同。
还有就是每次做压测或者等待golang 完全gc都要耗费不少时间,这样也会排查增加难度。
来源:https://segmentfault.com/a/1190000019929993


猜你喜欢
- 前言继老干妈、辣条之后,中国美颜软件在海外一夜成名,把所有人都幻化成了粉红小甜心。人类学者克里斯特尔·阿比丁博士认为,
- # -*- coding: utf-8 -*- import numpy as npimport matplotlib.pyplot as
- 天在刷题的时候用到了正则,用的过程中就感觉有点不太熟练了,很久没有用正则都有点忘了。所以现在呢,我们就一起来review一下python中正
- 描述Bootstrap Button(按钮)JavaScript 插件允许您加强按钮的功能。您可以控制按钮的状态,也可以为组件创建按钮组,比
- 使用定时功能对于我们想要快速获取某个数据来说,是一个非常好的方法。这样我们就不用苦苦守在电脑屏幕前,只为蹲到某个想要的东西。在之前我们已经讲
- 很多开发人员在使用MySQL时经常会在部分列上进行函数计算等,导致无法走索引,在数据量大的时候,查询效率低下。针对此种情况本文从MySQL5
- 自从腾讯微博上线以来,基本上就开始用了,一直到现在,作为一个开发人员,也看到了腾讯微博一直在不停的改变,也不知道大家有没有发现,腾讯微博提供
- 昨天第一次用python画圆,当时并没有安装numpy库(导入数据包)和matplotlib库(导入图形包),于是尝试用pip安装库首先,我
- 前言之前学python时在网上找了好多小程序,由于年代久远,已经忘记出自哪里了,给代码加了点注释,再稍微修改了一下,让代码的可读性更好,如有
- 用analyze进行处理,定期进行处理ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tb1_name
- 前言在很多网站中,基本上的都会有一个开头和一个结尾,在每一个网页中都会显示。相对于这种的来说,在Django中,最好的方法就是使用inclu
- str='python String function'生成字符串变量str='python String func
- 本文实例讲述了Python打印scrapy蜘蛛抓取树结构的方法。分享给大家供大家参考。具体如下:通过下面这段代码可以一目了然的知道scrap
- 一. np.dot()1.同线性代数中矩阵乘法的定义。np.dot(A, B)表示:对二维矩阵,计算真正意义上的矩阵乘积。对于一
- 安装redis服务1 下载redis cd /usr/local/ 进入安装目录 wget http://downl
- python语言本身没有提供const,但实际开发中经常会遇到需要使用const的情形,由于语言本身没有这种支出,因此需要使用一些技巧来实现
- 先判断是jquery对象还是html对象, 如果是jquery对象, 可以直接用 jquery对象.attr("
- 今天写了个小功能,看起来挺简单,写的过程中发现了些坑。1.div没有disabled的属性,所以得写成button2.disabled在da
- 错误提示Invalid byte 1 of 1-byte UTF-8 sequence原因分析在中文版的window下java的默认的编码为
- <td width="15%" align="left">是否为分包人&l