我们在3.1中说了,内存的分配是进程通过malloc()申请得到的,申请之后,并不会立刻为其分配物理内存,而是在首次访问的时候,通过缺页异常陷入内核中分配内存

那么既然是进程申请得到的,那么就可能在管理过程中,出现各种各样的事故

比如,

1.没有正确的回收,导致泄漏

2.访问的是已经分配内存边界外的地址,导致程序异常退出

那么我们就看一下常见的内存泄露的排查和定位

首先进程的内存区域,分为了只读端,数据段,堆,栈以及文件映射端

我们如果申请了一个整数数组 int data[64],定义了一个可以存储64个整数的内存段,这是一个局部变量,会从内存空间的栈中分配内存

栈空间的内存由系统管理,如果程序的运行超过了这个局部变量的作用域,这个栈内存就会被自动回收,不会产生内存泄露问题

堆空间则是最容易产生内存泄露的地方了,因为如果没有在运行的时候合理使用free()释放的话,就会造成内存泄露

其他的区域,比如只读段,因为是只读的,故不会泄露,数据段,因为比如全局变量和静态变量,因为在定义的时候就确定了大小,也不会产生泄露

最后内存映射端,包括动态链接库和共享内存,其中共享内存由程序动态分配管理,所以如果忘了回收,就会导致和堆内存一样的泄露问题

内存泄露,会导致系统内存的耗尽

虽然系统由OOM机制杀死进程,但是导致的性能问题是不可逆的

那么我们如何发现内存泄露并处理呢?

如果存在一个计算斐波那契数列的方法,我们去定位并处理其

运行其程序,并尝试观察其内存情况,这次利用工具是vmstat

利用vmstat去进行查询

# 每隔3秒输出一组数据

$ vmstat 3

procs ———–memory———- —swap– —–io—- -system– ——cpu—–

r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st

procs ———–memory———- —swap– —–io—- -system– ——cpu—–

r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st

0  0      0 6601824  97620 1098784    0    0     0     0   62  322  0  0 100  0  0

0  0      0 6601700  97620 1098788    0    0     0     0   57  251  0  0 100  0  0

0  0      0 6601320  97620 1098788    0    0     0     3   52  306  0  0 100  0  0

0  0      0 6601452  97628 1098788    0    0     0    27   63  326  0  0 100  0  0

2  0      0 6601328  97628 1098788    0    0     0    44   52  299  0  0 100  0  0

0  0      0 6601080  97628 1098792    0    0     0     0   56  285  0  0 100  0  0

在其中,free一列的数值在不断的变化,buffer和cache基本不变

未使用的内存在逐渐减少,而buffer和cache不变,说明系统使用内存一直升高

这不能说明是一定有内存泄露,也可能有应用程序在稳定申请所需内

这时候我们需要查看是否存在内存一直增长的进程,然后尝试处理

一个良好的内存泄露检测工具,memleak 可以跟踪系统或者进程的内存分配,释放请求,然后统计给出一个未释放内存和相应调用栈的汇总情况

$ /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]

memleak的输出可以看到,案例不断的分配内存,这些分配的地址并没有被回收

其中说明了是fibonacci()函数导致的分配内存没有释放

那么查看代码,果然是创建了函数,但是没有free

long long *fibonacci(long long *n0, long long *n1)

{

//分配1024个长整数空间方便观测内存的变化情况

long long *v = (long long *) calloc(1024, sizeof(long long));

*v = *n0 + *n1;

return v;

}

void *child(void *arg)

{

long long n0 = 0;

long long n1 = 1;

long long *v = NULL;

for (int n = 2; n > 0; n++) {

v = fibonacci(&n0, &n1);

n0 = n1;

n1 = *v;

printf(“%dth => %lld\n”, n, *v);

sleep(1);

}

}

child()调用了fibonacci函数,但是没有释放返回的内存

只需要在print之后,加上free()函数即可

那么我们总结一下

一般来说,我们会使用标准函数malloc()来申请内存,但是别忘了在使用之后,调用free释放掉

现实往往更加的复杂,因为

往往需要异常捕获的时候去释放

在多线程中异步释放

第三方类库中,可能还需要显式释放

最后就是今天说的memleak工具

发表评论

邮箱地址不会被公开。 必填项已用*标注