前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >案例分享-libc STL 造成的疑似“内存泄漏”

案例分享-libc STL 造成的疑似“内存泄漏”

作者头像
程序员小王
发布2020-10-29 10:19:53
1.7K0
发布2020-10-29 10:19:53
举报
文章被收录于专栏:架构说架构说

案例分享-libc STL 造成的疑似“内存泄漏”

现象描述:

我维护的一组服务器程序出现了严重的内存泄漏,32GB的内存,几天就跑满了

最近几天努力了一下,终于找到原因所在,并解决了问题。

我的程序根据我的计算,内存使用只需要30MB左右。但是观察发现,程序的内存不断上涨。

初步排查

刚开始就认为发生了,内存泄漏。

于是乎valgrind登场,但是检测了一遍发现,代码层面没有内存泄漏的东西。

但是内存上涨确是不争的事实。为此我怀疑valgrind可能不够准确(现在想想,这个怀疑是错误的)。

于是乎,我就开始查看代码,一行一行的读关键代码,还好代码不是很多,要不然就要崩溃了。

我查看了一下代码中用到new的地方,除了两个map,一个保存session,一个保存数据块,始终存在于进程中外,其他的都是局部变量,会自动释放掉的。

关于session的map, 我初期的工作就是发现被动关闭session的时候,session的信息会从map删掉,主动关闭的session则不会,修复了此BUG,但是内存还是上涨。

数据块的map,我仔细看了代码实在是看不出有什么问题,超过x 分钟的数据块会被删掉,当数据块的总大小超过设置的数据缓冲区时,也会删除早期的数据块。

map的元素都按时按规则删除了,怎么内存还是上涨的。

我一直都无法确定到底问题出在哪儿了。

我现在也忘记我当初是怎么找到原因的了。

心里就觉得map erase掉的元素,可能没有及时delete掉(元素是智能指针,计数器什么的)

在浑浑噩噩查找内存泄漏一天之后,我坐上了回去公交车。

我就拿手机搜了map,内存泄漏。

就那么神奇的出现了,map和glibc被联系到了一起。

我使劲打开博文,发现glibc的内存分配回收机制的确会导致疑似内存泄漏的现象。

确定问题

glibc的malloc分配的内存在free之后,并不一定会交还给操作系统,释放的内存会被glibc管理维护,方便下次malloc的时候继续使用。是有满足一定的条件的情况下,释放的内存才会交还给操作系统。

在不断的malloc,free之后,某一进程会导致大量的内存碎片产生,这些内存碎片由于glibc的回收机制,很难被 交还给操作系统。

画外音:如果申请内存大于128k,free之后os马上释放,如果brk方式申请的,需要移动指针方式释放。这个时候并不一定会交还给操作系统

因而出现“内存泄漏”现象。我的程序也是符合此规律的,内存是缓慢上涨的,但是我的程序实例多,所以内存的消耗的速度还是挺快的。

既然glibc不行,那么有没有其他的内存申请释放库呢?

答案是有,而且是肯定的。

我当时就搜到了 tcmalloc(google), jemalloc(facebook).

好吧,我是搜到的,我之前是不知道的。

看了一下网上的介绍,这两个的出现的主要目的是解决glibc的分配内存低效的问题(多线程环境下)。

我当时就想了,要是用tcmalloc或者jemalloc能不能解决这个内存碎片的问题呢。

实验发现,tcmalloc不行(我的实验环境不行,网上有其他人说可以)

jemalloc也不行,虽然内存上涨了速度慢了好多,但是还是会上涨,不能从根本上解决问题

在使用tcmalloc的过程中, 我发现了MallocExtension::instance()->GetHeapGrowthStacks(string )(google perftool的工具,跟tcmalloc一起安装的)

可以发现进程的增长状态。

我的代码:

代码语言:javascript
复制
string str;

MallocExtension::instance()->GetHeapGrowthStacks(&str);

  WriteStringToFile(str,"./heap1");

在一个线程中,定期调用。

代码语言:javascript
复制
然后在命令行:pprof --text ./进程名 heap1

即可解析出内存的增长状况,函数名也可以打印出来哦,哈哈(debug版的进程)

我就是这么做的哈。

然后发现我的内存增长就是数据块的map部分啊。

至此我还以为是内存泄漏了

我就打印数据块的释放日志,erase和delete是一一对应的。唉。

至此我终于确定,我释放了数据块,但是碎片我释放不掉。

看着glibc, 再看看tcmalloc和jemalloc。头大了,怎么办呢。

解决办法:这个不靠谱

继续搜,发现glibc有一个malloc_trim函数可以强制将glibc保存的待用内存给释放掉,于是我就自己定期释放了,问题解决。

在解决内存泄漏问题的过程中,我获得如下几点认识:

  1. 不要盲目相信glibc, 它虽然是系统默认的,却不一定就好。glibc和stl搭档可能有问题
  2. 要多阅读多了解,要是早点知道tcmalloc和jemalloc就会少走很多很弯路了。
  3. valgrind这个东西,怀疑它是可以的,但不要一直怀疑它,精品就是精品
  4. googleperftool的工具还是要学习一下的,它也是精品
  5. glibc 还有很多东西可以学习,malloc_trim只是一个,还有其他的反应内存分配状况的函数(这是手工释放呀) 我觉得glibc提供malloc_trim就是一个极好的设计。我能说tcmalloc和jemalloc都没有这个函数么?他们太自信了。认为不需要这个功能吧。
  6. 作为linux的程序员,系统,内核了解一下,深入了解一下没有坏处。
  7. 遇到事情要执着啊,解决问题会有很大的收获的。尝试尝试再尝试,累了歇歇继续尝试。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Offer多多 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 案例分享-libc STL 造成的疑似“内存泄漏”
    • 现象描述:
      • 初步排查
        • 确定问题
          • 解决办法:这个不靠谱
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档