1. 内存泄漏
内存泄漏(memory leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
特点
- 隐蔽性 因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷
- 积累性 内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。最直观的问题就是为什么我们的程序开始运行正常,过段时间就异常退出。
内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于使用错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存未释放而浪费掉。
产生的原因
“
我们在进行程序开发的过程使用动态存储变量时,不可避免地面对内存管理的问题。程序中动态分配的存储空间,在程序执行完毕后需要进行释放。没有释放动态分配的存储空间而造成内存泄漏,是使用动态存储变量的主要问题。一般情况下,作为开发人员会经常使用系统提供的内存管理基本函数,如malloc、realloc、calloc、free等,完成动态存储变量存储空间的分配和释放。但是,当开发程序中使用动态存储变量较多和频繁使用函数调用时,就会经常发生内存管理错误。
”
2. 如何排查内存泄漏
“
我们平时开发过程中不可避免的会遇到内存泄漏问题,这是常见的问题。既然发生了内存泄漏,我们就要排查内存泄漏的问题。想必大家也经常会用到以下排查内存问题的工具,如下:
”
- memwatch
- mtrace
- dmalloc
- ccmalloc
- valgrind
- debug_new
“
今天木荣不是介绍上面的排查工具,而是向大家介绍另一个内存泄漏排查工具:AddressSanitizer(ASan)。它支持 Linux、OS、Android等多种平台,不止可以检测内存泄漏,它是一个内存错误检测工具,可以检测很多常见的内存问题。
”
常见的内存问题检测:
- 内存泄漏
- 越界访问
- 使用了释放的内存
3. AddressSanitizer(ASan)工具
“
Address Sanitizer(ASan) 是一个快速的内存错误检测工具。它非常快,只拖慢程序两倍左右(比起Valgrind快多了)。它包括一个编译器instrumentation模块和一个提供malloc()/free()替代项的运行时库。从gcc 4.8开始,AddressSanitizer成为gcc的一部分。当然,要获得更好的体验,最好使用4.9及以上版本,因为gcc 4.8的AddressSanitizer还不完善,最大的缺点是没有符号信息。
”
使用方法:
- 用-fsanitize=address选项编译和链接你的程序。
- 用-fno-omit-frame-pointer编译,以得到更容易理解stack trace。
- 可选择-O1或者更高的优化级别编译
gcc -fsanitize=address -o main -g main.c
登录后复制
内存泄漏
#include void Fun() { char *pM = malloc(10); } int main(int argc, char *argv[]) { Fun(); return 0; }
登录后复制
编译输出
内存越界
- 堆栈内存越界
#include #include void Fun() { char *pM = malloc(10); } int main(int argc, char *argv[]) { //Fun(); int *array = malloc(10*sizeof(int)); if(array) { memset(array, 0, 10*sizeof(int)); } int res = array[10]; free(array); return 0; }
登录后复制
运行输出
ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ gcc -fsanitize=address -o main -g main.c ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ ls main main.c ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ ./main ================================================================= ==3234==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x000000400854 bp 0x7ffccc9253d0 sp 0x7ffccc9253c0 READ of size 4 at 0x60400000dff8 thread T0 #0 0x400853 in main /home/ubuntu/workspace_ex/Linux/ASan/main.c:16 #1 0x7fafc87a883f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f) #2 0x4006f8 in _start (/home/ubuntu/workspace_ex/Linux/ASan/main+0x4006f8) 0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8) allocated by thread T0 here: #0 0x7fafc8bea602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602) #1 0x4007f7 in main /home/ubuntu/workspace_ex/Linux/ASan/main.c:11 #2 0x7fafc87a883f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f) SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ubuntu/workspace_ex/Linux/ASan/main.c:16 main Shadow bytes around the buggy address: 0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa] 0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe ==3234==ABORTING
登录后复制
- 全局内存越界
#include #include int g_nArray[10] = {0}; void Fun() { char *pM = malloc(10); } int main(int argc, char *argv[]) { //Fun(); /* int *array = malloc(10*sizeof(int)); if(array) { memset(array, 0, 10*sizeof(int)); } int res = array[10]; free(array); */ int nIndex = g_nArray[10]; return 0; }
登录后复制
运行输出
ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ gcc -fsanitize=address -o main -g main.c ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ ls main main.c ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ ./main ================================================================= ==4196==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000601128 at pc 0x00000040083e bp 0x7ffc8a332540 sp 0x7ffc8a332530 READ of size 4 at 0x000000601128 thread T0 #0 0x40083d in main /home/ubuntu/workspace_ex/Linux/ASan/main.c:26 #1 0x7fda216a583f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f) #2 0x400718 in _start (/home/ubuntu/workspace_ex/Linux/ASan/main+0x400718) 0x000000601128 is located 0 bytes to the right of global variable 'g_nArray' defined in 'main.c:4:5' (0x601100) of size 40 SUMMARY: AddressSanitizer: global-buffer-overflow /home/ubuntu/workspace_ex/Linux/ASan/main.c:26 main Shadow bytes around the buggy address: 0x0000800b81d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b81e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b81f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0000800b8220: 00 00 00 00 00[f9]f9 f9 f9 f9 f9 f9 00 00 00 00 0x0000800b8230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800b8270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe ==4196==ABORTING ubuntu@ubuntu:~/workspace_ex/Linux/ASan$
登录后复制
使用释放的内存
#include #include #include int g_nArray[10] = {0}; int *p; void Fun() { char *pM = malloc(10); } void Fun1() { int nVar = 0; p = &nVar; } int main(int argc, char *argv[]) { //Fun(); int *array = malloc(10*sizeof(int)); if(array) { memset(array, 0, 10*sizeof(int)); } int res = array[10]; free(array); array[0] = 1; return 0; }
登录后复制
运行输出
ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ gcc -fsanitize=address -o main -g main.c ubuntu@ubuntu:~/workspace_ex/Linux/ASan$ ./main ================================================================= ==4954==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x000000400b68 bp 0x7ffefbe3b170 sp 0x7ffefbe3b160 READ of size 4 at 0x60400000dff8 thread T0 #0 0x400b67 in main /home/ubuntu/workspace_ex/Linux/ASan/main.c:28 #1 0x7f1bbb78983f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f) #2 0x400928 in _start (/home/ubuntu/workspace_ex/Linux/ASan/main+0x400928) 0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8) allocated by thread T0 here: #0 0x7f1bbbbcb602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602) #1 0x400b0b in main /home/ubuntu/workspace_ex/Linux/ASan/main.c:23 #2 0x7f1bbb78983f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f) SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ubuntu/workspace_ex/Linux/ASan/main.c:28 main Shadow bytes around the buggy address: 0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa] 0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe ==4954==ABORTING ubuntu@ubuntu:~/workspace_ex/Linux/ASan$
登录后复制
AddressSanitizer能检测的错误类型
错误类型 | 错误描述 |
---|---|
(heap) Use after free | 访问堆上已被释放的内存 |
Heap buffer overflow | 堆上缓冲区访问溢出 |
Stack buffer overflow | 栈上缓冲区访问溢出 |
Global buffer overflow | 全局缓冲区访问溢出 |
Use after return | 访问栈上已被释放的内存 |
Use after scope | 栈对象使用超过定义范围 |
Initialization order bugs | 初始化命令错误 |
Memory leaks | 内存泄漏 |
- 更多细节请访问官网
具体可以看 google 的官方文档:https://github.com/google/sanitizers/wiki/AddressSanitizer
结束语
“
ASan是个很好的检测内存问题的工具,不需要配置环境,使用还方便,编译时只需要-fsanitize=address -g 就可以, 运行程序时候可以选择添加对应的 ASAN_OPTIONS 环境变量就可以检测出很多内存问题。如果哪里不清楚可以查看官方说明,欢迎交流学习。
”
以上就是Linux下内存问题排查利器的详细内容,更多请关注小闻网其它相关文章!
评论(0)