当前位置:首页 > 奇闻趣事

nanosleep valgrind排查内存泄露

前言

C/C++运行效率高,操作系统内核和有性要求的程序(如游戏引擎)都要求用C/C++编写。其实C/C++强大的一点就是可以使用指针自由控制内存的使用,及时申请内存和释放内存,让其他编程语言高效运行。然而,内存管理是一把双刃剑。用的好,手臂会断的。如果应用堆上的内存没有得到及时有效的释放,就会造成内存泄漏。随着时间的推移,程序将耗尽系统内存,导致系统运行问题。就像你每天去图书馆借十几本书,直到图书馆关门才还。C语言中申请内存和释放内存的方法是malloc和free。C++与C兼容,所以可以使用malloc和free,而在面向对象的情况下可以使用new和delete,可以自动执行构造函数和析构函数。

如何发现内存泄漏

内存泄漏一般不会造成程序崩溃,所以比较隐晦,但是查找内存泄漏的方法也很简单,就是让程序运行一段时间,然后依次检查内存变化,通过Task Manager (windows)或者top(unix/linux)监控某个进程的内存变化更方便。有些程序内存泄漏相对较小,但找到它们的内存泄漏只是时间问题。这是一个有内存泄漏的程序的内存变化时间图。可见其内存占用总体上是在增加的

在内存泄漏较大的情况下,机器的cpu利用率飙升,cpu的等待百分比增加。从上面可以看到,交换内存的使用在不断增加,kswap进程不时出现在进程列表中。在linux中,watch-n1 "PS-o vsz-p

Valgrind定位内存泄漏

Valgrind是用于构建动态分析工具的工具框架。Valgrind工具可以自动检测很多内存管理和线程错误,并对程序进行详细分析。也可以使用Valgrind构建新工具。Valgrind的默认工具是内存检测,除此之外还有其他监控功能,这里就不讨论了。

valgrind的安装

linux下debian/ubuntu派系的安装方法:

itcast@ubuntu:~$ sudo apt install valgrindredhat/centos下[itcast@localhost ~]$ sudo yum install valgrind

valgrind的使用

下面是一个测试用C程序的例子

#include <。stdlib.h>。

#include <。stdio.h>。

void func()

{

//只申请内存不释放

void * p = malloc(sizeof(int));

}

int main()

{

func();

getchar();

返回0;

}

编译程序

gcc -o ./a.out ./main.c

使用valgrind命令执行程序,并将日志输出到文件中

val grind-log-file = ValRePOrt-leak-check = full-show-可达=yes - leak-resolution=low。/a.out

–log-log-file = val report指定在当前执行目录中生成分析日志文件,文件名为val report–leak-check = full以显示每个泄漏的详细信息–show-可达= yes是否检测控制范围之外的泄漏,如全局指针和静态指针,并显示所有内存泄漏类型–leak-resolution =低内存泄漏报告整合级别

最终执行输出的内容在下面的报告中解释,其中==98725==指的是流程编号。如果程序以多进程方式执行,将显示多个进程的内容。第一段是valgrind的基本信息。

==97825== Memcheck, a memory error detector==97825== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.==97825== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info==97825== Command: ./a.out==97825== Parent PID: 97615==97825==

第二段是堆内存分配的总结信息,提到程序一共申请了三次内存,其中两次被释放,分配了2052字节

==97825====97825== HEAP SUMMARY:==97825== in use at exit: 4 bytes in 1 blocks==97825== total heap usage: 3 allocs, 2 frees, 2,052 bytes allocated==97825==

第三段的内容描述了内存泄漏的具体信息,其中一个内存块占用4个字节。当调用malloc时,我们可以看到func函数最终在调用堆栈中调用了malloc,所以这些信息准确地定位了我们泄漏的内存被应用的位置。

==97825== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1==97825== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==97825== by 0x4006C7: func (in /home/itcast/workspace/memleak/a.out)==97825== by 0x4007E8: main (in /home/itcast/workspace/memleak/a.out)

最后一段是总结,4字节是内存泄漏

==97825====97825== LEAK SUMMARY:==97825== definitely lost: 4 bytes in 1 blocks==97825== indirectly lost: 0 bytes in 0 blocks==97825== possibly lost: 0 bytes in 0 blocks==97825== still reachable: 0 bytes in 0 blocks==97825== suppressed: 0 bytes in 0 blocks==97825====97825== For counts of detected and suppressed errors, rerun with: -v==97825== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Mallinfo手动打印内存信息定位

内存泄漏的位置基本上可以通过valgrind的日志来定位,在valgrind的日志中可以清楚的看到new和delete或者malloc和free不能一一对应。第二种方式是通过日志来观察,每次调用可疑接口后,可以调用mallinfo函数来打印当前进程占用的内存量。如果通过日志文件发现当前进程的内存使用不断增加,则可以认为可疑接口是内存泄漏的罪魁祸首。这样可以不断缩小怀疑的范围,直到最终定位到泄露的代码。对上述程序进行必要的调整

#include <。stdlib.h>。

#include <。stdio.h>。

#include <。malloc.h>。

void func()

{

printf(" func n ");

void * p = malloc(sizeof(int));

//免费(p);

}

void displayMallInfo()

{

struct mall info = mall info();

printf(" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = n ");

printf("arena:%dn ",info . arena);

printf("ordblks:%dn ",info . ordblks);

printf("smblks:%dn ",info . SMB lks);

printf("hblks:%dn ",info . hbl ks);

printf("hblkhd:%dn ",info . hblkhd);

printf("usmblks:%dn ",info . usmblks);

printf("uordblks:%dn ",info . uordblks);

printf("fordblks:%dn ",info . Ford blks);

printf("keepcost:%dn ",info . keep cost);

printf(" = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = n ");

}

int main()

{

displayMallInfo();

func();

displayMallInfo();

getchar();

返回0;

}

通过重新编译正在运行的程序,您可以看到以下信息:

itcast @ Ubuntu:~/workspace/mem leak $。/a.out

===========================

竞技场:0

ordblks:1

smblks:0

hblks:0

hblkhd:0

usmblks:0

uordblks:0

for blks:0

keepcost:0

===========================

功能

===========================

竞技场:135168

ordblks:1

smblks:0

hblks:0

hblkhd:0

usmblks:0

uordblks:1072

福特布鲁克:134096

keepcost:134096

===========================

要用这种方法总结某个函数的内存分配,需要熟悉linux中虚拟内存分配的原理。malloc在linux中的实现还调用了系统接口brk、sbrk、mmap等。linux实现内存应用解释:arena使用sbrk分配malloc(单位字节)内存总大小,普通(即非fastbin)空空闲块的ordblks数。smblks fastbin空闲块的数量(参见mallopt(3))。(不使用字典)hblks当前使用mmap(2)分配的块数。(见mallopt(3)中关于M_MMAP_THRESHOLD的讨论)。)hblkhd当前使用mmap(2)分配的块中的字节数。usmblks在空之间分配“高水位线”,即最大值(此字段未使用,始终为0),分配量在空之间。此字段仅在非线程环境中维护。fsmblks fastbin空空闲块中的总字节数。(未使用字典)为使用uordblks而分配的总字节数。fordblks 空空闲块中的总字节数。

监控与系统内存相关的调用

一般我们的操作环境中没有安装valgrind,所以这种情况valgrind无法跟踪,所以只能通过日志来跟踪这种情况。如果日志打印不出来,可以用另一种更简单的方式来进行:直接在strace中进行内存泄漏的过程。

strace -p <。pid>。

根据strace的内容,在内存大量泄漏的进程中,有很多系统调用申请系统内存,如下所示:

itcast @ Ubuntu:~/workspace/mem leak $ sudo strace-p 97495

itcast的[sudo]密码:

附工艺97495

restart _ syscall(& lt;...恢复中断的纳米睡眠...>。) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa933374000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa933273000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa933172000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa933071000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa932f70000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa932e6f000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa932d6e000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa932c6d000

nano LEEP({ 1,0},0x7fff275b1160) = 0

mmap(NULL,1052672,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fa932b6c000

nano LEEP({ 1,0},{0,297924314}) =?ERESTART_RESTARTBLOCK(被信号中断)

- SIGINT {si_signo=SIGINT,si_code=SI_KERNEL} -

++被SIGINT ++杀死

可以看出,每次程序调用malloc,系统的api实际上都是调用mmap来申请虚拟内存。这里根据系统调用brk前后的几次系统调用,大致确定应用内存在代码中的大概位置,然后通过检查代码就可以确定泄漏。

重写malloc以实现自定义跟踪

如果能给自己程序的每个malloc和free call添加一些自定义代码,配合一些跟踪记录的手段,也可以自己实现内存泄漏跟踪。以下是一个例子

#定义自由(p) {

printf(" # % s:% d:% s():free(0x % LX) n ",__FILE__,__LINE_,

__func__,(无符号长)p);

免费(p);

}

#定义malloc(大小)({

void *ptr=malloc(大小);

printf(" # % s:% d:% s():malloc(0x % LX) n ",__FILE__,__LINE_,

__FUNCTION__,(无符号长)ptr);

ptr

})

编译程序后,您可以看到以下日志输出

# main . c:19:func():malloc(0x1b 61420)

# main . c:20:func():free(0x1b 61420)

如果地址信息可以在每个malloc进程中记录在链表之类的容器中,那么每次记录都可以从空闲中移除,还没有释放的内存可以在最后的程序执行后打印出来。但是这种方法的局限性在于只能对自己编写的代码生效。如果调用第三方库,并且在第三方库中使用malloc和free,则此方法无法跟踪第三方库的内存泄漏。Malloc和free可以通过在C语言中定义宏来重新定义,new和delete运算符可以在C++中重载来实现相同的功能,这里就不多讨论了。

总结

上述方法适用于内存泄漏快,每次泄漏多的情况。如果内存一次只泄漏一点点,就很难跟踪,只能依靠valgrind工具辅助或者依靠代码读取进行故障排除。此外,STL导致的内存泄漏也是一个相对容易忽视的问题,也是最难排查的问题。之前我们用string.append()保存日志信息,不断追加,最后一步写到本地磁盘,释放请求的内存。但是由于没有创建本地磁盘的相关目录,写入失败,内存没有释放,导致内存泄漏,服务器停机。

1.《nanosleep valgrind排查内存泄露》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《nanosleep valgrind排查内存泄露》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/guonei/1118067.html

上一篇

上海浦东机场排查出1例确诊病例 真相到底是怎样的?

下一篇

苹果掉出全球手机市场前三 到底是什么状况?

印度宣布禁用43款移动应用程序 具体是什么情况?

印度宣布禁用43款移动应用程序 具体是什么情况?

边肖在百度热搜榜上看到,印度宣布禁止43款移动应用的热度相当高。从热度来看,大部分网民关注的是印度宣布禁播43款手机应用,所以并不是每个网民都知道印度宣布禁播43款手机应用的来龙去脉。想必不了解具体情况的网友一定想了解印度...

2020 QQ开发者大会上海举行,小程序+小游戏+美化平台悉数亮相 登上网络热搜了!

2020 QQ开发者大会上海举行,小程序+小游戏+美化平台悉数亮相 登上网络热搜了!

11月20日,2020 QQ开发者大会在上海召开,500多位QQ生态开发者齐聚一堂,见证并讨论QQ生态开放的最新进展和成果。腾讯副总裁兼腾讯QQ负责人朱良表示:“QQ将向开发者开放更多核心资源,共同创造更多用户价值,让年轻...

太原软件开发 太原做一个小程序需要多少费用?

太原软件开发 太原做一个小程序需要多少费用?

太原做一个小程序需要多少钱? 未来几年,小程序在移动互联网领域将会有巨大的商机。率先推出小程序,抢占流量红利,可以实现收益增长。  太原做一个小程序需要多少钱?下面回答一下问题。 微信小程序要求开发者基于微信官方开放界面权限进行开发。这意味着我们的商家如果想开发自己的小程序,需要一个懂相应开...

魔百盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了

  • 魔百盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了
  • 魔百盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了
  • 魔百盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了

魔百和网络机顶盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了

  • 魔百和网络机顶盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了
  • 魔百和网络机顶盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了
  • 魔百和网络机顶盒投屏 华为魔百盒好用吗?球球大家不要再买1G内存的电视盒子了
武器贸易条约 中国已完成加入《武器贸易条约》法律程序

武器贸易条约 中国已完成加入《武器贸易条约》法律程序

据外交部网站消息,外交部发言人赵7日举行例行记者会。记者招待会记录如下: 台长郭光:中阿合作论坛第九届部长级会议昨天成功举行。当前,全球抗疫和经济复苏仍面临严峻挑战,中国和阿拉伯国家都面临更大的外部压力和挑战。在这样的背景下,中国举办这次会议有哪些考虑?会议在促进中阿务实合作方面取得了哪些具...

将欲辞君挂帆去下一句 古诗词里的友情,海内存知己,天涯若比邻。

将欲辞君挂帆去下一句 古诗词里的友情,海内存知己,天涯若比邻。

可怜的交通银行 杜甫[唐] 翻手云遮手雨,薄而轻。 但是,你看,古代人管仲和富人穷人君子的鲍叔牙,却被抛弃得像粪土一样。 步行回家 杜甫[唐] 明朝全盛时期,经济采取了豪迈的姿态。 如果国家的国家是今天,武定就麻烦了。 凤翔官数千,饱餐一顿,衣马不能再轻肥。 清朝最困的人,捡了光头的残骸后,...

做app的软件叫什么 开发 | 创服科技告诉你如何选择合适的APP/H5/小程序载体!

  • 做app的软件叫什么 开发 | 创服科技告诉你如何选择合适的APP/H5/小程序载体!
  • 做app的软件叫什么 开发 | 创服科技告诉你如何选择合适的APP/H5/小程序载体!
  • 做app的软件叫什么 开发 | 创服科技告诉你如何选择合适的APP/H5/小程序载体!