系统优化-内存-常用的内存泄露的排查方案

linux动态分配内存经常会遇到俩个问题:

  1. 内存没有及时回收,发生内存泄露
  2. 访问的是已分配边界外的内存,造成内存越界

这章我们来讲下内存泄露、以及排查内存泄露的方法

内存分配以及回收

我们的内存主要分为以下部分、堆、栈、只读端、数据段、内存映射段。

  • 栈:主要存放局部变量,由系统统一管理,所以不需要主动回收内存,一旦程序的作用域超过局部变量,就需要在堆中动态分配内存,在栈中的内存不会导致内存泄露
  • 堆:程序通过malloc申请内存,除非程序退出否则申请的内存不会释放。这部分内存会导致内存泄露
  • 只读段:保存程序的代码和常量,由于不需要继续申请内存,这部分会导致引起内存泄露
  • 数据段:保存静态变量和全局变量,由于申请时候已经知道了大小所以也不需要释放,这部分会导致引起内存泄露
  • 内存映射段:保存动态链接库和共享内存,由于共享内存的存在,

    综上,堆、和内存映射段会导致内存泄露

    内存泄漏的危害非常大,这些忘记释放的内存,不仅应用程序自己不能访问,系统也不能把它们再次分配给其他应用。内存泄漏不断累积,甚至会耗尽系统内存。虽然oom会Kill程序,但是在kill程序前会有很多严重的问题

如何定位内存泄露

我们可以使用bcc-tools下的memleak,如图下面就是一个简单的内存泄露场景,child会分配内存而且没有回收

1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker cp app:/app /app
$ /usr/share/bcc/tools/memleak -p $(pidof app) -a
Attaching to pid 12512, Ctrl+C to quit.
[03:00:41] Top 10 stacks with outstanding allocations:
addr = 7f8f70863220 size = 8192
addr = 7f8f70861210 size = 8192
addr = 7f8f7085b1e0 size = 8192
addr = 7f8f7085f200 size = 8192
addr = 7f8f7085d1f0 size = 8192
40960 bytes in 5 allocations from stack
fibonacci+0x1f [app]
child+0x4f [app]
start_thread+0xdb [libpthread-2.27.so]

解决后的如下面所示

1
2
3
4
5
# 重新执行 memleak工具检查内存泄漏情况
$ /usr/share/bcc/tools/memleak -a -p $(pidof app)
Attaching to pid 18808, Ctrl+C to quit.
[10:23:18] Top 10 stacks with outstanding allocations:
[10:23:23] Top 10 stacks with outstanding allocations:

由mMemleak在centos中需要内核4.1以上支持,所以在老系统中我们用到valgrind的memcheck

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 valgrind  --tool=memcheck --leak-check=summary ./xxx-api

==20474==
==20474== HEAP SUMMARY:
==20474== in use at exit: 4,064 bytes in 8 blocks
==20474== total heap usage: 16 allocs, 8 frees, 4,288 bytes allocated
==20474==
==20474== LEAK SUMMARY:
==20474== definitely lost: 0 bytes in 0 blocks
==20474== indirectly lost: 0 bytes in 0 blocks
==20474== possibly lost: 4,032 bytes in 7 blocks
==20474== still reachable: 32 bytes in 1 blocks
==20474== suppressed: 0 bytes in 0 blocks
==20474== Rerun with --leak-check=full to see details of leaked memory
==20474==
==20474== For counts of detected and suppressed errors, rerun with: -v
==20474== Use --track-origins=yes to see where uninitialised values come from
==20474== ERROR SUMMARY: 28 errors from 4 contexts (suppressed: 0 from 0)

上面的definitely lost代表必然的内存泄露,他包括indirectly【非直接】和directly【直接俩种, possibly lost是可能的内存泄露。我们只需要查看definitely lost即可