C++单元测试之三--内存泄漏问题检查

简介: 在程序的性能指标中,内存是一个很重要的方面。内存问题包括很多方面:内存泄漏,非法指针使用(包括很常见的问题--使用未初始化指针),数组越界,栈溢出和奇地址访问等。这些问题在C/C++语言当中尤为明显,因为很少有其他语言比如java提供指针直接访问地址和内存空间。
在程序的性能指标中,内存是一个很重要的方面。内存问题包括很多方面:内存泄漏,非法指针使用(包括很常见的问题--使用未初始化指针),数组越界,栈溢出和奇地址访问等。这些问题在C/C++语言当中尤为明显,因为很少有其他语言比如java提供指针直接访问地址和内存空间。
而内存问题越早发现,越好解决,给产品带来的负面影响也越小。今天我就给大家介绍一个很简单的在单元测试阶段发现内存问题的方法。这就是使用valgrind(在linux平台),使用MALLOCDEBUG在AIX平台或使用Purify在Solaris平台。
修改Makefile如下(Linux平台):

点击(此处)折叠或打开

  1. OBJS= Main.o A.o

  2. UTest.out: $(OBJS)
  3. g++ -o UTest.out $(OBJS) -lgcov

  4. CURDIR=$(shell pwd)

  5. $(OBJS): %.o: %.cpp
  6. g++ -I(CURDIR)cgfprofilearcsftestcoverage

  7. #unit test and code coverage check
  8. UT:UTest.out
  9. valgrind --leak-check=full ./UTest.out
  10. ./gcov_checker 90 --objects $(OBJS)

  11. .PHONY: clean
  12. clean:
  13. rm -f *.o *.gcno *.gcda *.gcov UTest.out
修改A.cpp使其有内存泄漏:

点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>


  3. int A::Sum(int a, int b)
  4. {
  5.     return a+b;
  6. }

  7. int A::Multiply(int a, int b)
  8. {
  9.         char *p = new char[16];
  10.     return a*b;
  11. }


编译运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --leak-check=full ./UTest.out
  7. ==5578== Memcheck, a memory error detector.
  8. ==5578== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5578== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5578== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5578== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5578== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5578== For more details, rerun with: -v
  14. ==5578==
  15. Sum test passed!
  16. Multiply test passed!
  17. ==5578==
  18. ==5578== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 1)
  19. ==5578== malloc/free: in use at exit: 16 bytes in 1 blocks.
  20. ==5578== malloc/free: 3 allocs, 2 frees, 720 bytes allocated.
  21. ==5578== For counts of detected errors, rerun with: -v
  22. ==5578== searching for pointers to 1 not-freed blocks.
  23. ==5578== checked 92,988 bytes.
  24. ==5578==
  25. ==5578== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
  26. ==5578== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
  27. ==5578== by 0x8048C39: A::Multiply(int, int) (A.cpp:12)
  28. ==5578== by 0x8048B23: main (Main.cpp:14)
  29. ==5578==
  30. ==5578== LEAK SUMMARY:
  31. ==5578== definitely lost: 16 bytes in 1 blocks.
  32. ==5578== possibly lost: 0 bytes in 0 blocks.
  33. ==5578== still reachable: 0 bytes in 0 blocks.
  34. ==5578== suppressed: 0 bytes in 0 blocks.
  35. ==5578== Reachable blocks (those to which a pointer was found) are not shown.
  36. ==5578== To see them, rerun with: --show-reachable=yes
  37. ./gcov_checker 90 --objects Main.o A.o
  38. Code coverage:
  39. Main.cpp:66.67% - WARNING: code coverage
  40. A.cpp:100.0%
  41. See *.gcov files for details.
可以看出valgrind确实检测出了内存泄漏位于A::Multiply函数中,且在A.cpp的第12行。

不仅如此,valgrind还可以检测出其他很多内存问题,比如数组越界,非法地址访问等。
比如我们修改A.cpp如下:


点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>
  3. #include string.h>


  4. int A::Sum(int a, int b)
  5. {

  6.     return a+b;
  7. }

  8. int A::Multiply(int a, int b)
  9. {
  10.     char *p1;
  11.     strcpy(p1, "hello");

  12.     return a*b;
  13. }

编译运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --leak-check=full ./UTest.out
  7. ==5742== Memcheck, a memory error detector.
  8. ==5742== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5742== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5742== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5742== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5742== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5742== For more details, rerun with: -v
  14. ==5742==
  15. Sum test passed!
  16. ==5742== Use of uninitialised value of size 4
  17. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  18. ==5742== by 0x8048AD3: main (Main.cpp:14)
  19. ==5742==
  20. ==5742== Invalid write of size 4
  21. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  22. ==5742== by 0x8048AD3: main (Main.cpp:14)
  23. ==5742== Address 0x0 is not stack'd, malloc'd or (recently) free'd
  24. ==5742==
  25. ==5742== Process terminating with default action of signal 11 (SIGSEGV)
  26. ==5742== Access not within mapped region at address 0x0
  27. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  28. ==5742== by 0x8048AD3: main (Main.cpp:14)
  29. ==5742==
  30. ==5742== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 15 from 1)
  31. ==5742== malloc/free: in use at exit: 0 bytes in 0 blocks.
  32. ==5742== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
  33. ==5742== For counts of detected errors, rerun with: -v
  34. ==5742== All heap blocks were freed -- no leaks are possible.
  35. make: *** [UT] Segmentation fault


再比如数组越界:

点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>
  3. #include string.h>


  4. int A::Sum(int a, int b)
  5. {

  6.     return a+b;
  7. }

  8. int A::Multiply(int a, int b)
  9. {
  10.     
  11.         char *array = new char[100];
  12.         for(int i = 0; i 101; i++)
  13.                 array[i] = i;
  14.     delete [] array;




  15.     return a*b;
  16. }


运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --tool=memcheck --leak-check=full ./UTest.out
  7. ==5876== Memcheck, a memory error detector.
  8. ==5876== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5876== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5876== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5876== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5876== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5876== For more details, rerun with: -v
  14. ==5876==
  15. Sum test passed!
  16. ==5876== Invalid write of size 1
  17. ==5876== at 0x8048C9D: A::Multiply(int, int) (A.cpp:21)
  18. ==5876== by 0x8048B53: main (Main.cpp:14)
  19. ==5876== Address 0x401908C is 0 bytes after a block of size 100 alloc'd
  20. ==5876== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
  21. ==5876== by 0x8048C69: A::Multiply(int, int) (A.cpp:19)
  22. ==5876== by 0x8048B53: main (Main.cpp:14)
  23. Multiply test
  24. ==5876==
  25. ==5876== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 1)
  26. ==5876== malloc/free: in use at exit: 0 bytes in 0 blocks.
  27. ==5876== malloc/free: 3 allocs, 3 frees, 804 bytes allocated.
  28. ==5876== For counts of detected errors, rerun with: -v
  29. ==5876== All heap blocks were freed -- no leaks are possible.
  30. ./gcov_checker 90 --objects Main.o A.o
  31. Code coverage:
  32. Main.cpp:66.67% - WARNING: code coverage 90.00%
  33. A.cpp:100.0%
  34. See *.gcov files for details.


《返璞归真--UNIX技术内幕》在全国各大书店及网城均有销售:

京东
亚马逊                          China pub
上学吧                          1号店





目录
打赏
0
0
0
0
1
分享
相关文章
玩转C++内存管理:从新手到高手的必备指南
C++中的内存管理是编写高效、可靠程序的关键所在。C++不仅继承了C语言的内存管理方式,还增加了面向对象的内存分配机制,使得内存管理既有灵活性,也更加复杂。学习内存管理不仅有助于提升程序效率,还有助于理解计算机的工作原理和资源分配策略。
|
1月前
|
c与c++的内存管理
再比如还有这样的分组: 这种分组是最正确的给出内存四个分区名字:栈区、堆区、全局区(俗话也叫静态变量区)、代码区(也叫代码段)(代码段又分很多种,比如常量区)当然也会看到别的定义如:两者都正确,记那个都选,我选择的是第一个。再比如还有这样的分组: 这种分组是最正确的答案分别是 C C C A A A A A D A B。
26 1
|
8月前
|
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
1829 6
C++/C的内存管理
本文主要讲解C++/C中的程序区域划分与内存管理方式。首先介绍程序区域,包括栈(存储局部变量等,向下增长)、堆(动态内存分配,向上分配)、数据段(存储静态和全局变量)及代码段(存放可执行代码)。接着探讨C++内存管理,new/delete操作符相比C语言的malloc/free更强大,支持对象构造与析构。还深入解析了new/delete的实现原理、定位new表达式以及二者与malloc/free的区别。最后附上一句鸡汤激励大家行动缓解焦虑。
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
308 68
如何检查 Linux 内存使用量是否耗尽?
何检查 Linux 内存使用量是否耗尽?
268 58
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
257 0
什么是内存泄漏?C++中如何检测和解决?
大家好,我是V哥。内存泄露是编程中的常见问题,可能导致程序崩溃。特别是在金三银四跳槽季,面试官常问此问题。本文将探讨内存泄露的定义、危害、检测方法及解决策略,帮助你掌握这一关键知识点。通过学习如何正确管理内存、使用智能指针和RAII原则,避免内存泄露,提升代码健壮性。同时,了解常见的内存泄露场景,如忘记释放内存、异常处理不当等,确保在面试中不被秒杀。最后,预祝大家新的一年工作顺利,涨薪多多!关注威哥爱编程,一起成为更好的程序员。
174 0
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
195 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等