记一次golang memory leak的解决过程
在开发完一个类似于prometheus的metrics收集系统软件后,经过n多次debug,软件终于run起来了。因为对于我们的系统,指标不需要非常频繁的获取(事实上对于prometheus而言,也基本都是5秒、10秒或者更长时间的获取时间间隔),所以我首先调整了获取频率到50ms,准备先一次性看一下有没有性能问题。
果不其然,CPU处于合理的范围(30%的样子),但是内存,每秒大几MB的涨,不一会儿就涨到了1G多,这里说的内存时实际使用内存,也就是RES,并不是虚拟内存。而换算来看,节点的数据都还没获取到多少呢。
这明显时不能接受的。因为这个程序会像prometheus一样,作为守护进程长期运行。于是开始调查。利器上来了,pprof
。追了一会后,发现是我们存储每个metrics的label_name-label_value的那个map,分配了非常大量的内存,这个内存在本次数据被序列化到文件之前,是常驻的。只能进行优化处理了。(对于pprof关于内存leak的定位和过程,就不在这里赘述了,分享一篇文章:How I investigated memory leaks in Go using pprof on a large codebase)。
找到问题后,没有立即下手对该mmp,哦不对,是map进行开刀,而是仍然感觉即便是频繁的创建,非常不合理。仔细回顾了整个过程,一个细节引起了我的注意。就是由于程序是有很多锁、原子操作、goroutine的较为复杂的程序,在编译时用了-race
的选项,以便在运行的时候观察是否有资源的不合理竞争。
马上,将该选项移除后编译运行,内存增速果然急剧下降,这里开心一秒钟。但马上就变脸了,因为观察发现内存的增速还是有点猛。依旧用pprof定位到是该map。
没想到golang的map这么RG,到这里,我总结了下该地方使用map的原因,以及特点:
- 该map存储了是metrics的label。对于我们的metrics,目前只有很少的label,而且可预见的未来,也只会有几个,不到5个吧。
- 使用map,是为了加快搜索。
想到这,我想到了2种解决方案:
a. 将该map用slice替换,因为存储的数量很少,甚至都可以在搜索的时候暴力遍历,连二分查找都不用;
b. 在一个统一的地方用一个数据结构维护所遇到的所有label name和label value,也可以理解为symbol吧。我们系统的label name就是固定的几个,而label value,感觉顶多也就几千台机器,几万个吧。这个数据结构包含了map[string]uint16
和symbols的slice
。然后在每个metrics种依然用map,只是不再用string
,key和value都换成uint16
,指示在slice
中的下标。这样,不管是从id找string,还是string找id,都是O(1)的操作。
方案a没有去尝试,但一定是一个非常节省内存的方式。没有尝试的理由是不想引入复杂度和太多的数据结构,暴力搜索又显得不是很优雅。上面分享的那个文章中,采用的是这个方法。
采用了方案b,最终执行下来的结果还是比较满意。
其实具体怎么做,是和业务息息相关的。
总结
- golang的map是hash存储,查找速度是O(1)的,但是内部采用了空间换时间的做法,维护2个hash表,再加上一些辅助信息,占用的内存还是挺高的,不建议在程序中被频繁分配且被长时间持有。而且一个比较蛋疼的事情是它不像c++的map,是采用二叉树存储从而做到有序,golang的map是无须的,c++中专门又
unorderd_map
去做这个事情。使用的时候要注意了,可能会带来一些测试上面的不便捷。 go build -race
是个神器,但是它会带来额外的内存和cpu的增长。golang的map是非线程安全的,目前来看,编译器会分配额外的资源去监控这个map。
记一次golang memory leak的解决过程相关推荐
- IDEA中记一次BuildProject不好使的解决过程
场景 IDEA中进行修改部分代码后,需要点击编译按钮Build Project 近期点击后进行控制台就会提示Not Started,输出到一半就不输出了. 只能每次一修改部分代码就得重启项目. 实现 ...
- 记一次拉去代码失败的解决过程Permission denied, please try again. git@code.odrcloud.cn: Permission denied
问题描述: 今天想要从git上拉一份新的代码,首先使用idea拉去,失败报错没有权限,如下图: Cloning into 'xxx'... Permission denied, please try ...
- mysql memory leak_解决memory leak问题
应用程序注册了JDBC驱动,但当程序停止时无法注销这个驱动,tomcat为了防止内存溢出,就给强制注销了 解决: 重写了org.apache.commons.dbcp.BasicDataSource ...
- 一则JVM memory leak解决的过程
起因是我们的集群应用(3台机器)新版本测试过程中,一般的JVM内存占用 都在1G左右, 但在运行了一段时间后,慢慢升到了4G, 这是一个明显不正常的现象. 定位 过程: 1.先在该机器上按照步骤尝试重 ...
- This is very likely to create a memory leak.
前言 tomcat 7.0.78 java 1.8.0_161 现象 tomcat启动过程中出现如下报错: The web application [/xx/xxx] appears to have ...
- 使用LeakTracer检测android NDK C/C++代码中的memory leak
Memory issue是C/C++开发中比较常遇到,经常带给人比较大困扰,debug起来又常常让人无从下手的一类问题,memory issue主要又分为memory leak,野指针,及其它非法访问 ...
- MFCButton Memory leak(内存泄露问题)
MFCButton Memory leak(内存泄露问题) http://m.blog.csdn.net/blog/haoekin/8851219 1.无法显示右边箭头的问题 无论怎么折腾都没显示不出 ...
- 记一次golang中sync.Map并发创建、读取的问题
记一次golang中sync.Map并发创建.读取的问题 cunfate https://www.jianshu.com/p/f472e79909bc 背景: 我们有一个用go做的项目,其中用到了z ...
- 性能优化之内存泄露(Memory Leak)常用分析工具(另3种)
1 LeakCanary(最常用,能监控整个App内存泄漏情况) 1.1 使用LeakCanary // 仅在debug包启用LeakCanary debugImplementation 'com.s ...
- uniapp MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 upgrade listeners
uniapp 出现这个红色提示语 (node:39268) MaxListenersExceededWarning: Possible EventEmitter memory leak detecte ...
最新文章
- hdu 4876 ZCC loves cards(暴力)
- android 关闭蓝牙打电话功能,Android蓝牙开发【八】hfp接听、挂断电话
- 【Java】图解 Spring 循环依赖,写得太好了
- 优秀的程序员是如何诞生的?
- 鸿蒙会像IOS,你更看好鸿蒙还是苹果?iOS系统升级 分享与互联成苹果发布会主题...
- 中国智慧园区未来发展趋势及投资战略规划研究报告2022年版
- docker安装gitlab
- 年薪60w的程序员与年薪6w的极品程序员,差距怎么这么大呢?
- 经纬度的多种格式和转换方式
- 计算机网页设计实习报告怎么写,网页设计实习报告.docx
- labview dsn连接mysql_labview使用DSN与数据库的连接包括access,mysql
- Lnux-组-用户管理
- window10下搭建php的运行环境
- 搭建个人博客详细教程
- 如何推送PLC报警消息至微信
- 学习记录:关于Uniapp与Java实现支付宝沙箱APP内支付
- Java编程题之四个数字组成不同且无重复的三位数
- 受众同步管理 , 精准再营销
- 应用实践:Paddle分类模型大集成者[PaddleHub、Finetune、prompt]
- 知乎热议:如何成为中国科学院院士?