解决golang内存溢出的方法

作者:liaoyizhe 时间:2024-05-21 10:20:12 

最近在项目中出现golang内存溢出的问题,master刚开始运行时只有10多M,运行几天后,竟然达到了10多个G。而且到凌晨流量变少内存也没有明显降低,内存状态呈现一种很不健康的曲线。

解决golang内存溢出的方法

解决golang内存溢出的方法

像这种情况肯定是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/

解决golang内存溢出的方法

获取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一次。如果有内存泄漏,无非就是两种情况。

  1. 有goroutine泄漏,goroutine“飞”了,zombie goroutine没有结束,这个时候在这个goroutine上分配的内存对象将一直被这个僵尸goroutine引用着,进而导致gc无法回收这类对象,内存泄漏。

  2. 有一些全局(或者生命周期和程序本身运行周期一样长的)的数据结构意外的挂住了本该释放的对象,虽然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分钟左右。)

解决golang内存溢出的方法

发现问题所在

每次都会遗留这么大概0.5M的内存空间出来,就奇怪,明明整个goroutine退出为什么还有会内存占用?相应的全局变量也会删除该地方的引用。等一下,全局变量,难道是删除的时候没做好配对导致没有真正删除该引用吗?去查了下代码,果然是没有删除引用导致的,至此问题解决。

解决golang内存溢出的方法

这里面有个项目的坑,上报日志的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内存溢出的方法

还有就是每次做压测或者等待golang 完全gc都要耗费不少时间,这样也会排查增加难度。

来源:https://segmentfault.com/a/1190000019929993

标签:golang,内存溢出
0
投稿

猜你喜欢

  • python 判断矩阵中每行非零个数的方法

    2023-05-27 13:00:34
  • 深入了解python的tkinter实现简单登录

    2023-03-25 14:15:27
  • 详解Python flask的前后端交互

    2023-03-19 06:41:05
  • Python2与Python3的区别实例分析

    2021-01-07 11:47:17
  • python的几种开发工具介绍

    2021-09-28 01:54:44
  • 恢复master..xp_logattach(log explorer)

    2010-07-01 19:19:00
  • MySQL中ROUND函数进行四舍五入操作陷阱分析

    2024-01-15 04:02:04
  • Spring+MyBatis实现数据库读写分离方案

    2024-01-23 03:05:36
  • python使用urllib2提交http post请求的方法

    2023-11-24 19:35:53
  • python实现从字典中删除元素的方法

    2023-11-10 17:26:33
  • JQuery的Ajax请求实现局部刷新的简单实例

    2024-05-02 17:05:17
  • Python函数生成器原理及使用详解

    2023-12-02 17:57:31
  • 文字的减法

    2007-11-06 12:58:00
  • Python如何实现在字符串里嵌入双引号或者单引号

    2023-10-18 07:32:39
  • 如何配置一个稳定的SQL Server数据库

    2008-12-09 14:07:00
  • JavaScript中call,apply,bind的区别与实现

    2024-04-22 12:51:26
  • python分析apache访问日志脚本分享

    2021-08-12 04:20:09
  • SQL Server技巧之快速得到表的记录总数

    2011-01-04 14:36:00
  • 在Python中操作字符串之replace()方法的使用

    2021-03-15 14:10:59
  • 如何检测Oracle的ODBC是否连接成功?

    2009-11-24 20:31:00
  • asp之家 网络编程 m.aspxhome.com