当前位置:   article > 正文

【C/C++】内存泄漏检测(不借助工具)_c++ 在不借助外部工具的情况下如何排查内存泄漏

c++ 在不借助外部工具的情况下如何排查内存泄漏

一、内存泄漏是什么

        简单来说你 new 或者 malloc 一块内存后忘记 delete 或者 free 了,或者把地址丢失了导致无法进行释放。

以产生的方式来分类,内存泄漏可以分为四类:

  1. 常发性内存泄漏:发生内存泄漏的代码会被多次执行到,每次被执行时都会导致一块内存泄漏。

  2. 偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。

  3. 一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。

  4. 隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

二、如何检查

        内存泄漏检查对于经常在 Windows 系统或者 Linux 系统下编程的人员来说会相对来说比较容易,有各种各样的工具能够用来检查内存泄漏,但对于嵌入式工程师来说就非常麻烦了,因为也没有什么工具能够检查。这里提供一个不需要依赖工具,只需要添加几个代码文件即可检查出哪里发生了内存泄漏的代码。

1、my_malloc.c

  1. #include "my_malloc.h"
  2. static MY_ITEM g_record[SIZE];//如果存在多线程的情况一定要注意互斥操作!!!
  3. void* my_malloc(size_t n, const char* file, const int line)
  4. {
  5. void* ret = malloc(n);
  6. if (ret != NULL)
  7. {
  8. int i = 0;
  9. for (i = 0; i < SIZE; i++)
  10. {
  11. if (g_record[i].pointer == NULL)
  12. {
  13. g_record[i].pointer = ret;
  14. g_record[i].size = n;
  15. g_record[i].file = file;
  16. g_record[i].line = line;
  17. break;
  18. }
  19. }
  20. }
  21. return ret;
  22. }
  23. void my_free(void* p)
  24. {
  25. if (p != NULL)
  26. {
  27. int i = 0;
  28. for (i = 0; i < SIZE; i++)
  29. {
  30. if (g_record[i].pointer == p)
  31. {
  32. g_record[i].pointer = NULL;
  33. g_record[i].size = 0;
  34. g_record[i].file = NULL;
  35. g_record[i].line = 0;
  36. free(p);
  37. break;
  38. }
  39. }
  40. }
  41. }
  42. void print_dynamic_memory_info(void)
  43. {
  44. int i = 0;
  45. printf("动态内存申请尚未释放的如下:\n");
  46. printf("%-20s\t%-10s\t%s\n", "地址", "地址大小", "申请位置");
  47. for (i = 0; i < SIZE; i++)
  48. {
  49. if (g_record[i].pointer != NULL)
  50. {
  51. printf("%-20p\t%-10d\t%s:%d 行\n",
  52. g_record[i].pointer, g_record[i].size, g_record[i].file, g_record[i].line);
  53. }
  54. }
  55. }

2、my_malloc.h

  1. #ifndef __MY_MALLOC_H__
  2. #define __MY_MALLOC_H__
  3. #include <stdio.h>
  4. #include <malloc.h>
  5. #define SIZE 256
  6. typedef struct
  7. {
  8. void* pointer;
  9. int size;
  10. const char* file;
  11. int line;
  12. }MY_ITEM;
  13. void* my_malloc(size_t n, const char* file, const int line);
  14. void my_free(void* p);
  15. void print_dynamic_memory_info(void);
  16. #endif // !__MY_MALLOC_H__

3、my_malloc_inf.h

  1. #ifndef __MY_MALLOC_INF_H__
  2. #define __MY_MALLOC_INF_H__
  3. #include "my_malloc.h"
  4. #define malloc(n) my_malloc(n, __FILE__, __LINE__)
  5. #define free(p) my_free(p)
  6. //void* my_malloc(size_t n, const char* file, const int line);
  7. //void my_free(void* p);
  8. #endif // !__MY_MALLOC_INF_H__

三、如何使用

        只需将 #include "my_malloc_inf.h" 放到有动态申请内存的代码中即可(这同时也是它的不足之处)

        比如在著名开源项目 cJSON.h 中添加进 #include "my_malloc_inf.h" 。

  1. #include <stdio.h>
  2. #include "my_malloc_inf.h"
  3. #include "cJSON.h"
  4. void main()
  5. {
  6. char* str = (char*)malloc(sizeof(char) * 6);
  7. cJSON* json = cJSON_CreateNull();
  8. print_dynamic_memory_info();
  9. cJSON_Delete(json);
  10. print_dynamic_memory_info();
  11. }

四、优缺点

优点:

  • 无需借助工具,对嵌入式工程师来说非常友好。
  • 在运行的时候甚至可以将地址丢失的动态空间进行释放。
  • 正常使用 malloc() 函数与 free() 函数,无需改动原有代码。

缺点:

  • 对于工程量大的代码来说可能会比较不方便,因为头文件众多,需要添加的地方也很多。
  • 显示申请动态空间的代码位置太过深入,还是无法能够十分快速的找出哪里申请的空间没有释放。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/969392
推荐阅读
相关标签
  

闽ICP备14008679号