C++线上系统内存泄漏问题如何解决

x33g5p2x  于2021-10-17 转载在 C/C++  
字(2.4k)|赞(0)|评价(0)|浏览(316)

1. 如何判断系统是否出现了内存泄漏:

如何判断系统是否出现了内存泄漏:

  1. 出现内存泄漏问题,一种是泄漏严重,内存耗尽,此时的现象一定是服务器上的某些服务异常中断,开启打印后会看到类似于“内存申请失败”之类的log;
  2. 另一种情况可能是泄漏的速度较慢,这是需要定期的去检查一下系统上的内存情况,如果服务占用的内存超过了程序实际所需的内存大小,或者虚拟内存的占用一直在不断增长,则可以判定出现了内存泄漏。

当确认已出现内存泄漏的情况后,需要继续进行判断:

  1. 是自己写的业务代码 造成的内存泄漏;
  2. 还是第三方库的内存泄漏。

进行这样的判断比较容易,可以根据错误打印中申请内存的位置,或者gdb attach上去查看一下申请内存的位置,就可以大致看出是哪个位置引入的内存泄漏。

2. 如何解决内存泄漏的问题:

2.1 内存池:

内存池解决不了已经发生泄漏的线上系统的问题,主要做的是一种预防以及辅助定位的工作。

可以根据系统所使用到的内存的情况,将内存根据分配的大小等进行分类,并在malloc后对池子中的内存附加上一些专有的标记信息,在日志中记录malloc的位置。

这样,当出现内存泄漏时,就可以根据出现泄漏的内存的类型去走查对应部分的代码。

使用内存池时需要注意的是:

一定要使用多进程的方式,每个进程对应一个独立的内存池。

2.2 mtrace工具:

2.2.1 mtrace的使用简介:

mtrace是一个用于定位内存泄漏的工具,可以实现看到具体是哪一行代码malloc申请的内存没有被释放。

mtrace工具的主要思路是在我们的调用内存分配和释放的函数中装载“钩子(hook)”函数,通过“钩子”函数打印的日志来帮助我们分析对内存的使用是否存在问题。

对该工具的使用包括两部分内容:

  1. 一是要修改源码,装载hook函数(通过mtrace、muntrace);
  2. 另一个是通过运行修改后的程序,生成特殊的log文件,然后利用mtrace工具分析日志,判断是否存在内存泄漏以及定位可能发生内存泄漏的代码位置。

PS:“mtrace”这个名字既是指 mtrace() 这个函数,也是指用于分析log日志的工具。

mtrace() 函数原型:

#include <mcheck.h>

void mtrace(void);			//装载钩子
void muntrace(void);		//卸载钩子

函数的具体介绍可以参看 man 3 mtrace

其中,mtrace()用于开启内存分配跟踪,muntrace()用于取消内存分配跟踪。

具体的做法是 mtrace() 函数中会为那些和动态内存分配有关的函数安装“钩子(hook)”(例如malloc()/realloc()/memalign()/free()),这些钩子函数会为我们记录所有有关内存分配和释放的跟踪信息,而 muntrace() 则会卸载相应的钩子函数。

基于这些钩子函数的调试跟踪信息,就可以分许是否存在内存泄漏问题了。

2.2.2 mtrace的使用举例:

#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h>

int main(int argc, char **argv)
{
	mtrace();

	int *p = (int*)malloc(16);

	free(p);

	p = (int*)malloc(32);		//出问题的行,malloc申请内存后没有释放

	muntrace();

	return 0;
}

如果需要使用mtrace,则编译时需要加 -g 编译选项:

gcc -g test_memleak.c -o test_memleak

生成日志并分析定位问题:

首先设置生成日志文件的路径,具体的方法是通过定义并导出一个环境变量 MALLOC_TRACE :

export MALLOC_TRACE=./test.log

然后就可以直接运行可执行文件了:

./test_memleak

随后查看生成的日志文件:

$ cat test.log
= Start
@ ./test_leak:[0x400624] + 0x852450 0x10
@ ./test_leak:[0x400634] - 0x852450
@ ./test_leak:[0x40063e] + 0x852470 0x20
= End

日志中的三行分别对应源码中的三次 malloc、free、malloc 操作。

其中,每行的第一个数字对应源码的代码地址,+ 或 - 符号表示这一样是分配内存 或是 释放内存,第三个数字表示的是malloc()函数分配的内存的首地址。

使用 addr2line 工具可以反推出源文件的行数:

$ addr2line -f -e test_leak 0x400624
main
/home/u/samples/test_memleak.c:9

这样的方法适合代码量不大的小程序,对于大型程序,系统提供了一个叫做 mtrace 的命令行工具,可以帮助我们完成对日志的分析:

$ mtrace ./test_leak $MALLOC_TRACE
Memory not freed:
-----------------
           Address     Size     Caller
0x0000000000852470     0x20  at /home/u/samples/test_memleak.c:13

2.3 valgrind

Valgrind是一套Linux下的仿真调试工具的集合。Valgrind由内核以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具类似于插件(plug-in),利用内核提供的服务完成各种特定的内存调试任务。

Valgrind不仅可以用于定位程序中内存泄漏的问题,包括内存访问越界、重复释放等。

参考内容:
https://www.zhihu.com/question/63946754
http://tinylab.org/mtrace-memleak/

相关文章