1、内存的分配和回收
- 栈内存:由系统自动分配和管理。比如,程序中定义的局部变量。一旦程序运行超过了这个局部变量的作用域,栈内存就会被系统自动回收,不会产生内存泄漏
- 堆内存:由应用程序自己来分配和管理。除非程序退出,这些堆内存不会被系统自动释放,而是需要应用程序明确调用库函数 free() 来释放他们。否则会内存泄漏
- 只读段:包括程序的代码和常量。由于只读,不会再去分配内存,不会内存泄漏
- 数据段:包括全局变量和静态变量。在定义时已经确定大小,不会内存泄漏
- 内存映射段:包括动态链接库和共享内存。其中共享内存由程序动态分配和管理。如果程序忘记回收,会导致跟堆内存类似的内存泄漏问题。
虽然系统有 OOM(Out of Memory)机制杀死进程,但在其之前,引发的一连串反应,导致严重的性能问题。
比如,其他需要内存的进程,可能无法分配新的内存;内存不足,又会触发系统的缓存回收以及 SWAP 机制,从而进一步导致 I/O 的性能问题等等。
2、demo
1、准备
bcc工具
计算斐波那契数列的案例,来看看内存泄漏问题的定位和处理方法
斐波那契数列是一个这样的数列:0、1、1、2、3、5、8…,也就是除了前两个数是 0 和 1,其他数都由前面两数相加得到,用数学公式来表示就是 F(n)=F(n-1)+F(n-2),(n>=2),F(0)=0, F(1)=1。
docker run --name=app -itd feisky/app:mem-leak
2、分析
1、vmstat

可以看到 free不断下降,而buffer 和cache基本不变。说明系统使用的内存升高。
2、memleak

可以看到,进程 app 在不停地分配内存。并且找到是 fibonacci()函数分配的内存没有释放。
3、分析源码
docker exec app cat /app.c...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() 函数,但并没有释放 fibonacci() 返回的内存。所以,想要修复泄漏问题,在 child() 中加一个释放函数就可以了,比如:
void *child(void *arg)
{
...
for (int n = 2; n > 0; n++) {
v = fibonacci(&n0, &n1);
n0 = n1;
n1 = *v;
printf("%dth => %lld\n", n, *v);
free(v); // 释放内存
sleep(1);
}
}
