如何在linux下检测linux top 内存泄漏漏

Linux 下几款程序内存泄漏检查工具 - 博客频道 - CSDN.NET
youbingchen的博客
这个博客可能更新比较慢 ,主战场迁移到:/
分类:linux
写这篇博客的原因呢是因为自己在编写基于Nginx磁盘缓存管理程序,目前已经进入测试阶段,关于这个程序的测试分为几个主要步骤:
1.内存管理是否正确(因为这个程序本身开辟很多内存空间进行缓存管理,同时这个程序程序本身就是基于C/C++开发的,内存管理机制一直是程序员头痛的东西)
2.程序的健硕性如何(服务器任何程序的基本要求就是要满足高并发的要求,也就是说,如果达不到这个基本要求,程序并并不能成为服务器)
针对第一点,以下将介绍几款内存泄漏检查工具
一个强大开源的程序检测工具
GNU扩展,用来跟踪malloc,mtrace为内存分配函数(malloc,rellaoc,memalign,free)安装hook函数
用于检查C/C++内存泄漏的工具,即是检查是否存在程序运行结束还没有释放的内存,以一个运行库发布
和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域
一个跨平台的 C++ 内存泄漏检测器
也是一个动态库发布的形式,优点类似dmalloc,但是相比之下,可能特点少了一些
Electric Fence
不仅仅能够跟踪malloc()和free(),同时能够检查读访问以及写入,能够准确指出导致错误的指令
Valgrind详解
Valgrind包括以下一些工具:
1.Memcheck:这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够给发现开发中绝大多数的内存错误使用的情况,比如:使用未初始化
2.callgrind:它主要用来检查程序中函数中调用过程中出现的问题
3.cachegrind:它主要用来检查程序中缓存使用出现的问题
4.Helgrind:它主要用来检查多线程中出现的竞争问题
5.Massif:它主要用来检查程序中堆栈使用中出现的问题
6.Extension:可以使用core提供的 功能,自己编写特定的内存调试 工具
Linux程序内存空间布局
代码段(.text):这里存放的是CPU要执行的指令,代码是可共享的,相同的代码在内存中只有一份拷贝,同时这个段是只读的,防止程序由于错误而修改自身指令
初始化数据段(.data)。这里存放的是程序中需要明确赋初始值的变量,例如位于所有函数之外的全局变量:int val=100。需要强调的是,以上两段都是位于程序的可执行文件中,内核在调用exec函数启动该程序时从源程序文件中读入。
未初始化数据段(.bss)。位于这一段中的数据,内核在执行该程序前,将其初始化为0或者null。例如出现在任何函数之外的全局变量:以及未初始化或初值为0的全局变量和静态局部变量
堆(Heap)。这个段用于在程序中进行动态内存申请,例如经常用到的malloc,new系列函数就是从这个段中申请内存。
已初始化且初值非0的全局变量和静态局部变量
栈(Stack)。函数中的局部变量以及在函数调用过程中产生的临时变量都保存在此段中。可执行代码、字符串字面值、只读变量。
内存检查原理
Memcheck检测内存问题的原理图:
1.Valid-value表:
对于进程的整个地址空间中的每一字节(byte),都有与之对应的8个bits,对于CPU的每个寄存器,也有一个与之对应的bit向量。这些bits负责记录该字节或者寄存器值是否具有有效 的、已经初始化的值
2.Valid-Address表
对于进程整个地址空间中的 么一个字节(byte),还有与 之对应的1bit,负责记录该地址是否能够被读写。
当要读写内存中的某个字节时,首先检查这个字节对应的A bit。如果该A bit显示该位置是无效位置,memcheck则报告读写错误。
内核(core)类似于 一个虚拟的CPU的环境,这样当内存中的某个字节被加载到真实的CPU中时,该字节对应的V bit也被加载到虚拟的CPU环境中,一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序 的输出,则memcheck会检查对应的vbits,如果该值尚未初始化,则会报告使用未初始化内存错误。
接下来我主要是介绍valgrind的安装和使用,关于其他的工具,大家可以自己上网去查阅资料,谢谢配合!
Valgrind 安装
1.解压安装包
tar -jxvf valgrind-3.11.0.tar.bz2
-C /usr/local/src
2.进入目录安装
cd /usr/local/src/valgrind-3.11.0
3.运行./autogen.sh 设置环境(需要标准的autoconf工具)
./autogen.sh
4.配置Valgrind,生成MakeFile文件
./configure --prefix=/usr/local
5.编译和安装valgrind
&& make install
valgrind 使用
第一步:准备好程序
为了valgrind发现的错误更精确,如能够定位到源代码的行,建议在编译时加上-g参数,编译优化选项选择O0(不要优化)
第二步:在valgrind下,运行可执行程序
利用valgrind调试内存问题,不需要重新编译源程序,它的输入就是二进制的可执行程序。调用Valgrind的通用格式是:valgrind [valgrind-options] your-prog [your-prog-options]
Valgrind 的参数分为两类,一类是 core 的参数,它对所有的工具都适用;另外一类就是具体某个工具如 memcheck 的参数。Valgrind 默认的工具就是 memcheck,也可以通过“–tool=tool name”指定其他的工具。Valgrind 提供了大量的参数满足你特定的调试需求,具体可参考其用户手册。
利用Memcheck发现常见的内存问题总结
Memcheck将内存泄露分为两种,一种是可能的内存泄露(Possibly lost),另外一种是确定的内存泄露(Definitely lost)。Possibly lost 是指仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首地址。Definitely lost 是指已经不能够访问这块内存。而Definitely lost又分为两种:直接的(direct)和间接的(indirect)。直接和间接的区别就是,直接是没有任何指针指向该内存,间接是指指向该内存的指针都位于内存泄露处。在上述的例子中,根节点是directly lost,而其他节点是indirectly lost
youbingchen
排名:千里之外
(3)(3)(2)(1)(7)(0)(4)(1)(9)(6)(26)(4)(0)(0)(0)(1)您还可以使用以下方式登录
当前位置:&>&&>& > linux中内存泄漏的检测(四)记录泄漏的大小
linux中内存泄漏的检测(四)记录泄漏的大小
《linux中内存泄漏的检测(三)定制化的new/delete》讲到,利用C++的函数重载的特性,使C++的代码,也能方便地为new/delete加上用于检测内存泄漏的统计代码。然而,也因此引入的新的问题。目前的统计方式仅仅统计申请/释放内存的次数,并没有统计每次申请/释放内存的大小。这种方法对于C来说是够用了,因为在C中申请和释放的大小是相同的,而在C++中就不一定了。考虑以下两种情况:(1)申请了子类的空间却只释放了父类的空间father *pF =delete pF;构造子类的时候申请的是子类所需大小的空间,然后先初始化父类的成员,再初始化子类的成员。析构的时候,由于是父类的指针,只调用父类的析构函数并释放父类所占的空间。不是说多态吗?既然pF指针子类,为什么不调用子类的析构函数?因为多态的前提是虚函数。正常情况下类的析构函数都应该写成虚函数,如果忘了,就有可能造成内存泄漏。(2)申请了一个数组的空间却只释放第一项元素的空间class A *pA = new class[5];delete pA;也不是所有这样的情况都会导致内存泄漏,如果class是一个内置类型,像int, char这种,就没有问题。对于内置类型,只能说没有内存泄漏方面,但有可能会有其它未知的潜在问题,所以仍不建议这么写。在C++中,class就不限于内置类型了,如果是自己定义的类,delete pA只是释放pA所指向的数组的第一项,这样就产生了内存泄漏。由于以上原因,仅仅统计申请/释放的次数,还不能准确地检测内存泄漏的情况,因此,在申请/释放的同时,还要记录大小。大家在写代码的时候,有没有产生过这样的疑问,为什么申请内存时要传入所需要申请的内存大小,而释放时不需要说明释放多大的内存?那是因为在申请时,把所申请的大小记在了某个地方,释放时从对应的对方查出大小。那么记在什么地方呢?一般有两种方式:1 非入侵式,内存分配器自行先申请内存(和栈配合使用),用作记录用户层的申请记录(地址,大小)。 用户释放空间时会查找该表,除了知道释放空间大小外还能判断该指针是合法。2 入侵式,例如用户要申请1byte的内存,而内存分配器会分配5byte的空间(32位),前面4byte用于申请的大小。释放内存时会先向前偏移4个byte找到申请大小,再进行释放。两种方法各有优缺点,第一种安全,但慢。第二种快但对程序员的指针控制能力要求更高,稍有不慎越界了会对空间信息做成破坏。我们linux上的gcc/g++编译器默认使用入侵式,为了验证我们找到的地址是否存储了我们想要的数据,我写了这样的测试代码:#include #if(defined(_X86_) && !defined(__x86_64))#define _ALLOCA_S_MARKER_SIZE 4#elif defined(__ia64__) || defined(__x86_64)#define _ALLOCA_S_MARKER_SIZE 8#endifint main(void){
void * p = NULL;
int a = 5, n = 1;
while (a--)
p = new char[n];
size_t w = *((size_t*)((char*)p -
_ALLOCA_S_MARKER_SIZE));
cout&&&w = &&& w &&& n = &&<n<</n<这是运行结果:w = 33 n = 1w = 33 n = 10w = 113 n = 100w = 1009 n = 1000w = 10017 n = 10000当我们读取申请到的内存的前面几个字节时,查到的数据与真实申请的数据好像有关系,但是又总是略大一点。这是不是我们要找的数据呢?它和真实申请的大小有什么关系呢?这要从gcc的内存分配策略说起。假设现在要申请空间大小为n,实际分配的大小为m,我们读取到的值为k(1)当调用malloc申请n个大小的空间,编译器还会多分配_ALLOCA_S_MARKER_SIZE个字节用于存储这片空间的管理信息。在我所测试的centos 64上这个管理信息一共8个字节,上文提到的申请空间的大小的信息就在其中。那么m=n+_ALLOCA_S_MARKER_SIZE(2)为了减少内存碎片,实现申请的大小为一个数的整数倍,在我所测试的centos 64上测得这个数为16,即实际申请的大小为16的倍数。那么m=(n+8-1)&0xFFFFFFF0 + 0x10(3)为了避免申请过小的内存,有这样一个限定,最小的实际分配空间大小为0x20m = (n+8-1)&0xFFFFFFF0 + 0x10 if m & 0x20 m = 0x20(4)因为m一定为16的倍数,所以在二进制中m的最后四位始终为0,并不起作用。因此这4位用于做标准位。于是有k = m + 1总结m = (n+7)&0xFFFFFFF0 + 0x11 , k = m + 1为了证明这个结论是正确的,我写了这样的代码:#include #include#include#include #if(defined(_X86_) && !defined(__x86_64))#define _ALLOCA_S_MARKER_SIZE 4#elif defined(__ia64__) || defined(__x86_64)#define _ALLOCA_S_MARKER_SIZE 8#endifint main(void){
void * p = NULL;
srand(time(0));
int a = 100000;
while (a--)
int n = rand() % 10000;
p = new char[n];
size_t w = *((size_t*)((char*)p -
_ALLOCA_S_MARKER_SIZE));
if ( n &= 8) n = 9;
int n2 = ((n+7) & 0xFFFFFFF0) + 0x11;
assert(n2 == w);
return 0;}实际上我们在统计的时候并不关心调用者申请的大小,而是编译器真正申请和释放的大小,即,代码如下:#include #include #include #if(defined(_X86_) && !defined(__x86_64))#define _ALLOCA_S_MARKER_SIZE 4#elif defined(__ia64__) || defined(__x86_64)#define _ALLOCA_S_MARKER_SIZE 8#endifsize_t count = 0;extern &C&{void* __real_malloc(int c); void * __wrap_malloc(int size){
__real_malloc(size);
size_t w = *((size_t*)((char*)p -
_ALLOCA_S_MARKER_SIZE)) - 1;
cout&&&malloc &&<w<</w<现在我们分别针对以上提到的两种情况测试:(1)申请了子类的空间却只释放了父类的空间class father{
int *p1;public:
father(){p1 =}
~father(){delete p1;}};class son : public father{
int *p2;public:
son(){p2 =}
~son(){delete p2;}};int main(void){
count = 0;
father *p =
if(count != 0)
cout&&&memory leak!&&(2)申请了一个数组的空间却只释放第一项元素的空间class A{
int *p1;public:
~A(){delete p1;}};int main(void){
count = 0;
A *p = new A[5];
if(count != 0)
cout&&&memory leak!&&分析:方便性: 功能是否支持说明运行时检查否该方法要求运行结束时对运行中产生的打印分析才能知道结果。修改是否方便是wrap函数实现非常简单,且只需要实现一次,对所有参与链接的文件都有效使用是否方便是要关掉这一功能,只需要将这个链接选项去掉即可- 全面性:功能是否支持说明C接口是否可以统一处理否C的每个接口都需要分别写包装函数C++接口是否可以统一处理是&动态库与静态库的内存泄漏是否可以检测到是wrap是个链接选项,对所有通过wrap与__wrap_malloc和__wrap_free链接到一起的文件都起作用,不管是.o、.a或者.so- 准确性:功能是否支持说明是否会有检测不到的情况否&是否可以定位到行否&是否可以确定泄漏空间的大小是&就爱阅读网友整理上传,为您提供最全的知识大全,期待您的分享,转载请注明出处。
欢迎转载:
推荐:    
4【原】初步认识电脑linux下检测内存泄漏办法【广州新嘉华】【软件开发培训吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:15,053贴子:
linux下检测内存泄漏办法【广州新嘉华】收藏
达内软件开发培训机构,0元学软件开发,先就业后付款,学员就职IBM,用友等企业达内软件开发培训,高端软件课程,名师授课,专业团队教您从零学起!
本帖讨论下 linux 下的 C++ 程序的内存泄漏的检测方法及其实现进行探讨。其中包括 C++ 中的 new 和 delete 的基本原理。
内存检测的基本实现原理!!!
想检测内存泄漏,就必须对程序中的内存分配和释放情况进行记录,所能够采取的办法就是重载所有形式的operator new 和 operator delete,截获 new operator 和 delete operator 执行过程中的内存操作信息。
登录百度帐号推荐应用你正在使用的浏览器版本过低,将不能正常浏览和使用知乎。}

我要回帖

更多关于 linux c 内存泄漏 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信