searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

C/C++程序利用valgrind检测内存泄漏

2023-10-11 03:11:27
13
0

1.什么是内存泄漏

1.1内存泄漏

malloc等类似函数,申请内存后, 使用完毕,没有用free释放; 造成内存持续被占用着,不能被后续流程

重复使用; 大量内存泄漏后,造成系统内存紧张, 直直程序或系统崩溃.

1.2大概分类

1.单次泄漏

a. 函数有泄漏,但程序只会运行它一次, 如init函数

b.申请的全局内存,程序整个过程确实需要使用它,但是程序退出时没有主动释放(free)

单次泄漏问题不大,程序退出时,系统会全部释放,但还是写规范为秒.

2.持续泄漏

a.如while循环内持续申请内存,没有释放;这种比较容易发现; 

b.不易发现的是复杂流程中,某个函数有内存泄漏,且这个函数频繁调用到

c.c语言中通过传递内存指针,传递内存块到程序流程很远很间接的函数中,

   且有失败流程, 这个是最复杂且难以发现.

3.隐性泄漏

例如有一个指针链表, 程序申请了1M内存10000个,放到链表中,但是程序只需要最近的10个, 其他老内存都是一定不会再使用的.

但是程序退出时,根据链表释放了所有内存. 此时无法直接检测到内存泄漏,但是却有极多本应该释放的内存没有计时释放,侵占大量系统内存.

 

valgrind安装

yum install valgrind

 

程序正常退出的泄漏检测

 

运行

 

nohup valgrind --trace-children=yes --time-stamp=yes --leak-check=full --vgdb=full --num-callers=25 --log-file=memory03.log /opt/program aaa &

/opt/program 待检测程序,程序前面的参数是valgrind 的

aaa 程序的参数

num-callers=25 这个参数很有用, 是打印出内存泄漏位置的堆栈25层,默认15层,可能不够

 

让程序正常退出

kill -15 $(pidof valgrind)

注意这里信号15会通过valgrind转发给我们测试的程序,程序需要注册响应信号,让程序正常退出;

也可以注册其他信号如信号2.

不能发送信号9, 信号9会直接杀死valgrind,造成无法检测.

还有测试的程序不能强制退出,如果强制退出无法检测到正常的流程.

 

报告示例

==00:01:31:00.465 37477==

==00:01:31:00.465 37477== 389,790 (9,360 direct, 380,430 indirect) bytes in 90 blocks are definitely lost in loss record 16 of 16

==00:01:31:00.469 37477==    at 0x4C29F73: malloc (xxx.c:309)

==00:01:31:00.469 37477==    by 0x667AECD: xxx(unsigned long) (in xxx.so.2)

==00:01:31:00.469 37477==    by 0x6681C37: xxx::operator new(unsigned long) (in xxx.so.2)

==00:01:31:00.469 37477==    by 0x672E383: xxx (in xxx.so.2)

==00:01:31:00.471 37477==    by 0x410FD8: xxx(xxx.c:119)

==00:01:31:00.471 37477==    by 0x411AB6: xxx(xxx.c:375)

==00:01:31:00.471 37477==    by 0x5042DD4: start_thread (in /usr/lib64/libpthread-2.17.so)

==00:01:31:00.471 37477==    by 0x7FE1EAC: clone (in /usr/lib64/libc-2.17.so)

==00:01:31:00.471 37477==

==00:01:31:00.471 37477== LEAK SUMMARY:

==00:01:31:00.471 37477==    definitely lost: 9,360 bytes in 90 blocks

==00:01:31:00.471 37477==    indirectly lost: 380,430 bytes in 990 blocks

==00:01:31:00.471 37477==      possibly lost: 4,480 bytes in 7 blocks

==00:01:31:00.471 37477==    still reachable: 0 bytes in 0 blocks

==00:01:31:00.471 37477==         suppressed: 0 bytes in 0 blocks

==00:01:31:00.471 37477==

==00:01:31:00.471 37477== Use --track-origins=yes to see where uninitialised values come from

==00:01:31:00.471 37477== For lists of detected and suppressed errors, rerun with: -s

==00:01:31:00.471 37477== ERROR SUMMARY: 4618 errors from 39 contexts (suppressed: 0 from 0)

 

重点查看definitely(检测到一定发生泄漏的位置) 泄漏的位置,然后查看stack,看是哪里申请的内存,去修改代码.

 

运行过程中检测

上面介绍了,程序退出后,检测没有释放的内存.

这里介绍不退出程序,在程序运行中检测内存;但是在程序运行中检测内存的缺点是,确实会有很多内存没有释放,

但是这些内存有是程序运行中需要用到的,这个有点难以区分,需要自己把握.

例如,检测内存时,运行到"do something with a ",此时a内存没有释放, 会被认为内存泄漏.

void add(){
    char* a = malloc(100);
    //do something with a 
   free(a);
}

 

操作

首先已经在运行valgrind ..... + 程序

然后在另一个shell窗口执行命令,vgdb会向valgrind 通信,发送操作请求

增量变化

vgdb --pid=$(pidof valgrind) leak_check full reachable increased

所有变化量,包括增加和减少

vgdb --pid=$(pidof valgrind) leak_check full reachable changed

运行这个命令后会重置对比基点,再次运行后会对比变化量,以上次运行此命令时的内存为基点,计算变化量.

这个命令会把上面举例的报告直接打印到终端.

最后还是可以正常退出valgrind检测最终泄漏结果.

 

隐性泄漏

需要运行中检测,并且分析.

 

0条评论
0 / 1000
g****n
5文章数
0粉丝数
g****n
5 文章 | 0 粉丝
原创

C/C++程序利用valgrind检测内存泄漏

2023-10-11 03:11:27
13
0

1.什么是内存泄漏

1.1内存泄漏

malloc等类似函数,申请内存后, 使用完毕,没有用free释放; 造成内存持续被占用着,不能被后续流程

重复使用; 大量内存泄漏后,造成系统内存紧张, 直直程序或系统崩溃.

1.2大概分类

1.单次泄漏

a. 函数有泄漏,但程序只会运行它一次, 如init函数

b.申请的全局内存,程序整个过程确实需要使用它,但是程序退出时没有主动释放(free)

单次泄漏问题不大,程序退出时,系统会全部释放,但还是写规范为秒.

2.持续泄漏

a.如while循环内持续申请内存,没有释放;这种比较容易发现; 

b.不易发现的是复杂流程中,某个函数有内存泄漏,且这个函数频繁调用到

c.c语言中通过传递内存指针,传递内存块到程序流程很远很间接的函数中,

   且有失败流程, 这个是最复杂且难以发现.

3.隐性泄漏

例如有一个指针链表, 程序申请了1M内存10000个,放到链表中,但是程序只需要最近的10个, 其他老内存都是一定不会再使用的.

但是程序退出时,根据链表释放了所有内存. 此时无法直接检测到内存泄漏,但是却有极多本应该释放的内存没有计时释放,侵占大量系统内存.

 

valgrind安装

yum install valgrind

 

程序正常退出的泄漏检测

 

运行

 

nohup valgrind --trace-children=yes --time-stamp=yes --leak-check=full --vgdb=full --num-callers=25 --log-file=memory03.log /opt/program aaa &

/opt/program 待检测程序,程序前面的参数是valgrind 的

aaa 程序的参数

num-callers=25 这个参数很有用, 是打印出内存泄漏位置的堆栈25层,默认15层,可能不够

 

让程序正常退出

kill -15 $(pidof valgrind)

注意这里信号15会通过valgrind转发给我们测试的程序,程序需要注册响应信号,让程序正常退出;

也可以注册其他信号如信号2.

不能发送信号9, 信号9会直接杀死valgrind,造成无法检测.

还有测试的程序不能强制退出,如果强制退出无法检测到正常的流程.

 

报告示例

==00:01:31:00.465 37477==

==00:01:31:00.465 37477== 389,790 (9,360 direct, 380,430 indirect) bytes in 90 blocks are definitely lost in loss record 16 of 16

==00:01:31:00.469 37477==    at 0x4C29F73: malloc (xxx.c:309)

==00:01:31:00.469 37477==    by 0x667AECD: xxx(unsigned long) (in xxx.so.2)

==00:01:31:00.469 37477==    by 0x6681C37: xxx::operator new(unsigned long) (in xxx.so.2)

==00:01:31:00.469 37477==    by 0x672E383: xxx (in xxx.so.2)

==00:01:31:00.471 37477==    by 0x410FD8: xxx(xxx.c:119)

==00:01:31:00.471 37477==    by 0x411AB6: xxx(xxx.c:375)

==00:01:31:00.471 37477==    by 0x5042DD4: start_thread (in /usr/lib64/libpthread-2.17.so)

==00:01:31:00.471 37477==    by 0x7FE1EAC: clone (in /usr/lib64/libc-2.17.so)

==00:01:31:00.471 37477==

==00:01:31:00.471 37477== LEAK SUMMARY:

==00:01:31:00.471 37477==    definitely lost: 9,360 bytes in 90 blocks

==00:01:31:00.471 37477==    indirectly lost: 380,430 bytes in 990 blocks

==00:01:31:00.471 37477==      possibly lost: 4,480 bytes in 7 blocks

==00:01:31:00.471 37477==    still reachable: 0 bytes in 0 blocks

==00:01:31:00.471 37477==         suppressed: 0 bytes in 0 blocks

==00:01:31:00.471 37477==

==00:01:31:00.471 37477== Use --track-origins=yes to see where uninitialised values come from

==00:01:31:00.471 37477== For lists of detected and suppressed errors, rerun with: -s

==00:01:31:00.471 37477== ERROR SUMMARY: 4618 errors from 39 contexts (suppressed: 0 from 0)

 

重点查看definitely(检测到一定发生泄漏的位置) 泄漏的位置,然后查看stack,看是哪里申请的内存,去修改代码.

 

运行过程中检测

上面介绍了,程序退出后,检测没有释放的内存.

这里介绍不退出程序,在程序运行中检测内存;但是在程序运行中检测内存的缺点是,确实会有很多内存没有释放,

但是这些内存有是程序运行中需要用到的,这个有点难以区分,需要自己把握.

例如,检测内存时,运行到"do something with a ",此时a内存没有释放, 会被认为内存泄漏.

void add(){
    char* a = malloc(100);
    //do something with a 
   free(a);
}

 

操作

首先已经在运行valgrind ..... + 程序

然后在另一个shell窗口执行命令,vgdb会向valgrind 通信,发送操作请求

增量变化

vgdb --pid=$(pidof valgrind) leak_check full reachable increased

所有变化量,包括增加和减少

vgdb --pid=$(pidof valgrind) leak_check full reachable changed

运行这个命令后会重置对比基点,再次运行后会对比变化量,以上次运行此命令时的内存为基点,计算变化量.

这个命令会把上面举例的报告直接打印到终端.

最后还是可以正常退出valgrind检测最终泄漏结果.

 

隐性泄漏

需要运行中检测,并且分析.

 

文章来自个人专栏
C/C++
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0