加入收藏 | 设为首页 | 会员中心 | 我要投稿 河北网 (https://www.hebeiwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 建站 > 正文

如何通过wrap malloc定位C/C++程序的内存泄漏

发布时间:2019-09-29 14:50:55 所属栏目:建站 来源:我不想种地
导读:用C/C++开拓的措施执行服从很高,但却常常受到内存走漏的困扰。本文提供一种通过wrap malloc查找memory leak的思绪。 什么是内存走漏? 动态申请的内存丢失引用,造成没有步伐接纳它(我知道杠jing要说历程退出前体系会同一接纳),这即是内存走漏。 Java等编

用C/C++开拓的措施执行服从很高,但却常常受到内存走漏的困扰。本文提供一种通过wrap malloc查找memory leak的思绪。

C/C++

什么是内存走漏?

动态申请的内存丢失引用,造成没有步伐接纳它(我知道杠jing要说历程退出前体系会同一接纳),这即是内存走漏。

Java等编程说话会自动打点内存接纳,而C/C++必要显式的开释,有许多本领可以停止内存走漏,好比RAII,好比智能指针(大多基于引用计数计数),好比内存池。

理论上,只要我们足够警惕,在每次申请的时辰,都紧记开释,那这个天下就清净了,但实际每每没有那么柔美,好比抛非常了,开释内存的语句执行不到,又可能某菜鸟措施员不警惕埋了一个雷,以是,我们必需直面真实的天下,那就是我们会遭遇内存走漏。

怎么查内存走漏?

我们可以review代码,但从海量代码里找到潜匿的题目,这犹如大海捞针,每每两手空空。

以是,我们必要借助器材,好比valgrind,但这些找内存走漏的器材,每每对你行使动态内存的方法有某种等候,可能说束缚,好比常驻内存的工具会被误报出来,然后真正有效的信息会袒护在误报的汪洋大海里。许多时辰,乃至valgrind基础办理不了一般项目中的题目。

以是许多闻名的开源项目,为了能用valgrind跑,都费大力大举气,大幅修改源代码,从而使得项目切合valgrind的要求,满意这些要求,用vargrind跑完没有任何报警的项目叫valgrind干净。

既然这些玩意儿都中看不顶用,以是,求人不如求己,照旧得自力重生。

什么是动态内存分派器?

动态内存分派器是介于kernel跟应用措施之间的一个函数库,glibc提供的动态内存分派器叫ptmalloc,它也是应用最普及的动态内存分派器实现。

从kernel角度看,动态内存分派器属于应用措施层;而从应用措施的角度看,动态内存分派器属于体系层。

应用措施可以通过mmap体系直接向kernel申请动态内存,也可以通过动态内存分派器的malloc接口分派内存,而动态内存分派器会通过sbrk、mmap向kernel分派内存,以是应用措施通过free开释的内存,并不必然会真正返还给体系,它也有也许被动态内存分派器缓存起来。

google有本身的动态内存分派器tcmalloc,其它jemalloc也是闻名的动态内存分派器,他们有差异的机能示意,也有差异的缓存和分派计策。你可以用它们替代linux体系glibc自带的ptmalloc。

new/delete跟malloc/free的相关

new是c++的用法,好比Foo *f = new Foo,着实它分为3步。

  • 通过operator new()分派sizeof(Foo)的内存,最终通过malloc分派。
  • 在新分派的内存上构建Foo工具。
  • 返回新构建的工具地点。

new=分派内存+结构+返回,而delete则是便是析构+free。

以是搞定malloc、free就是从基础上搞定动态内存分派。

1. chunk

每次通过malloc返回的一块内存叫一个chunk,动态内存分派器是这样界说的,后头我们都这样称号。

2. wrap malloc

gcc支持wrap,即通过转达-Wl,--wrap,malloc的方法,可以改变挪用malloc的举动,把对malloc的挪用链接到自界说的__wrap_malloc(size_t)函数,而我们可以在__wrap_malloc(size_t)函数的实现中通过__real_malloc(size_t)真正分派内存,尔后我们可以做搞点小举措。

同样,我们可以wrap free。malloc跟free是配对的,虽然也有其他相干API,好比calloc、realloc、valloc,但这基础上照旧malloc+free,好比realloc就是malloc + free。

怎么去定位内存走漏呢?

我们会malloc各类差异size的chunk,也就是每种差异size的chunk会有差异数目,假如我们可以或许跟踪每种size的chunk数目,那就可以知道哪种size的chunk在走漏。很简朴,假如该size的chunk数目一向在增添,那它很也许走漏。

光知道某种size的chunk走漏了还不足,我们得知道是哪个挪用路径上导致该size的chunk被分派,从而去搜查是不是正确开释了。

怎么跟踪到每种size的chunk数目?

我们可以维护一个全局 unsigned int malloc_map[1024 * 1024]数组,该数组的下标就是chunk的size,malloc_map[size]的值就对应到该size的chunk分派量。

这便是维护了一个chunk size到chunk count的映射表,它足够快,并且它可以包围到0 ~ 1M巨细的chunk的范畴,它已经足够大了,试想一次分派一兆的块已经很可怕了,可以包围到大部门场景。

那大于1M的块怎么办呢?我们可以通过log记录下来。

  • 在__wrap_malloc里,++malloc_map[size]
  • 在__wrap_free里,--malloc_map[size]

很简朴,我们通过malloc_map记录了各size的chunk的分派量。

怎样知道开释的chunk的size?

差池,free(void *p)只有一个参数,我怎样知道开释的chunk的size呢?怎么办?

我们通过在__wrap_malloc(size_t)的时辰,分派8+size的chunk,也就是多分派8字节,开始的8字节存储该chunk的size,然后返回的是(char*)chunk + 8,也就是偏移8个字节返回给挪用malloc的应用措施。

这样在free的时辰,传入参数void* p,我们把p往前移动8个字节,解引用就能获得该chunk的巨细,而该巨细值就是前一步,在__wrap_malloc的时辰配置的size。

好了,我们真正做到记录各size的chunk数目了,它就存在于malloc_map[1M]的数组中,假设64个字节的chunk一向在被分派,数目一向在增添,我们认为该size的chunk很有也许走漏,那怎么定位到是那边挪用过来的呢?

怎样记录挪用链?

我们可以维护一个toplist数组,该数组假设有10个元素,它生涯的是chunk数最大的10种size,这个很轻易做到,通过对malloc_map取top 10就行。

然后我们在__wrap_malloc(size_t)里,测试该size是不是toplist之一,假如是的话,那我们通过glibc的backtrace把挪用仓库dump到log文件里去。

留意:这里不能再分派内存,以是你只能行使backtrace,而不能行使backtrace_symbols,这样你只能获得挪用仓库的标记地点,而不是标记名。

怎样把标记地点转换成标记名,也就是对应到代码行呢?

addr2line

addr2line器材可以做到,你可以追查到挪用链,进而定位到内存走漏的题目。

至此,你已经get到了整个焦点头脑。

虽然,现实项目中,我们做的更多,我们不只仅记录了toplist size,还记录了各size chunk的增量toplist,会记录大块的malloc/free,会wrap更多的API。

总结一下:通过wrap malloc/free + backtrace + addr2line,你就可以定位到内存走漏了。

(编辑:河北网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读