发生内存泄漏后

        内存泄漏是指程序在运行过程中分配的内存无法被释放,导致内存使用量不断增加,最终可能导致程序崩溃或系统崩溃。

产生内存泄漏的原因

内存泄漏可能是由多种原因造成的,例如:

  • 忘记释放内存。由于项目比较大,一般申请内存的地方与释放内存的地方并不在一起,导致容易遗漏。例如,在C语言中,如果使用malloc()函数分配了内存,就必须使用free()函数来释放它。否则,这块内存就会一直占用着,直到程序结束。
  • 智能指针循环引用。多个对象相互引用,导致它们都无法被释放。同样在项目比较大的时候,由于维护的智能指针比较多,关系复杂时容易出现。例如,如果一个对象A引用了对象B,而对象B又引用了对象A,那么这两个对象都无法被释放。
  • 全局变量:全局变量在程序的整个生命周期内都存在,即使它们不再被使用。在一定程度上来讲,也是一种泄漏。

        如果是正常的使用全局变量,那内存的占用自然是合理的。但对于一些其它原因导致的内存泄漏,那就让人很头大了。影响小一点的,也就是自身应用的功能异常,影响大了,可能导致整个系统崩溃。

怎么搞

         我写了一个模拟内存泄漏的小程序,来演示一下具体的排查过程,运行程序前,执行free是这样的:

root@gl:/home/gl# free
              total        used        free      shared  buff/cache   available
Mem:        2025004      109884     1792128          92      122992     1778336
Swap:       2097148       87868     2009280

        执行后:

root@gl:/home/gl# free
              total        used        free      shared  buff/cache   available
Mem:        2025004     1181228      711804          92      131972      702484
Swap:       2097148       84796     2012352

        由于模拟的小程序逻辑比较简单,就先不放出名字了。实际线上环境排查问题的时候,面临比较多的也是这样的一个场景:有比较多的应用跑在一台服务器上,开始时是一头雾水的。

        由free的输出可以看出,系统总内存为2025004KB,即2GB,原有空闲内存1792128KB,约1.7GB,运行异常应用后,剩余内存一下子缩水到了711804KB,仅700MB。并且是used列增长了近1GB的大小,反而buf/cache变化不大。持续观察一会,会发现可用内存还在持续减少。这个时候的你:

        这个时候,自然而然就能想到,需要找出是哪个进程占用内存比较多呢?

top:

        命令行窗口输入top,输出默认是按cpu占用排序的,按 M 切换为按内存占用排序:

top - 03:00:12 up 57 min,  3 users,  load average: 0.01, 0.01, 0.00
Tasks: 122 total,   2 running,  71 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.2 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2025004 total,   710588 free,  1182252 used,   132164 buff/cache
KiB Swap:  2097148 total,  2012608 free,    84540 used.   701384 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                    
   1615 root      20   0 1055448 1.005g   1312 S   0.0 52.0   0:00.68 a.out                      
    894 root      20   0 1411024  18612   5476 S   0.0  0.9   0:01.03 dockerd                    
    713 root      20   0 1284616  12804   5932 S   0.0  0.6   0:06.93 containerd                 
    403 root      19  -1   74340  10932  10596 S   0.0  0.5   0:00.24 systemd-journal            
   ...         

        可疑进程"a.out"映入眼帘,虚拟内存VIRT与RES常驻内存占用高大1GB,跟free看到的内存增量也是能对应的上的。接下来要做的,就是看一下a.out这个程序是不是真的内存泄漏了。

内存泄漏检测工具

        memleak是一个开源的内存泄漏检测工具,可以帮助我们检测C/C++程序中的内存泄漏。它可以跟踪程序的内存分配和释放情况,并报告任何可疑的内存泄漏。类似地,还有valgrind也可以用,但memleak的好处是不用重启进程,可以直接观察运行中的进程行为,所以这里用memleak。

        执行memleak命令,并通过-p指定可疑进程pid:

root@gl:/home/gl# memleak-bpfcc -p 1615
Attaching to pid 1615, Ctrl+C to quit.
[03:06:12] Top 10 stacks with outstanding allocations:
        10485760 bytes in 10 allocations from stack
                fun+0x1f [a.out]
                main+0x12 [a.out]
                __libc_start_call_main+0x80 [libc.so.6]
[03:06:17] Top 10 stacks with outstanding allocations:
        15728640 bytes in 15 allocations from stack
                fun+0x1f [a.out]
                main+0x12 [a.out]
                __libc_start_call_main+0x80 [libc.so.6]
^Croot@gl:/home/gl# 

        观察一会后CTRL+C停止掉,从输出中可以看出,memleak在截至 [03:06:12] 的时间点,检测到了10次内存分配,总计分配了10485760字节(即10MB),这些分配尚未被释放。紧接着,可以看到申请内存的堆栈:即main函数中的fun调用。

        接下来就比较简单了,找到a.out的源码分析一下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void fun() {
        int i = 0;
        while (i++ < 2048) {
                int* p = (int*)malloc(1024 * 1024);
                memset(p, 0, 1024 * 1024);
                *p = i;
                printf("%d MB\n", *p);

                if (i > 1024) {
                        sleep(1);
                }
        }
}

int main() {
        fun();
        pause();

        return 0;
}

        看到这里你估计就要笑了,这是哪个沙雕写出的代码?很明显可以看出,是由于fun函数中的malloc没有调用对应的free,我们补上free调用就可以了。然而实际项目中,代码看起来并不是这么直接的,申请后的内存是要拿来使用的,你得找到一个合适的地方才能释放它。这就可以喝杯茶慢慢再分析了~

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fireplusplus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值