• 分享一个辅助分析内存泄漏的脚本


      最近给系统做了一点优化,前几天去查看系统监控,想看看上线前后cpu使用率曲线变化情况。查看的时候意外发现上线前后内存占用相差不少,20%以上。

    本来我没怎么在意这个问题,因为我们系统会在运行过程中缓存部分数据内容。但客户觉得有异常,坚持要查。于是把一个月的内存使用情况调出来看,这一看就发现问题了:

      系统内存占用确实是在缓慢增加,一两天的内存使用率曲线看不出什么,但一个月的可以明显看出来,是一条斜率很小的直线。

      发现了有内存泄漏,但是想具体分析是哪个进程泄漏的还真不好办。因为我们系统有上千个进程在跑,而监控系统又只记录了总体内存占用情况,没记录单个进程内存占用。

      没有枪,没有炮,只能自己来造 :) 。 我设计了一个简单的分析方法:

      1 首先,我写一个脚本,每天定期记录系统所有进程使用情况,保存到文件,以时间戳命名。(用ps实现)

      2 脚本跑几天后,我再使用另一个脚本,把文件合并起来分析,把每个进程在不同时间点的内存使用情况合并成一行,逐个进程输出

      3 比较每个进程第一次出现与最后一次出现时占用内存之差,按从小到大排序,即可得出可能存在内存泄漏的程序。

      效果如下:可以看出这个进程内存占用一直在增加,从第一次统计到最后一次统计之间内存使用增加了1460k

      知道问题后就好办了,使用valgrind+gdb很快就找出导致内存泄漏的代码,纠正即可。

      下面是分析过程中用到的脚本,希望对大家有帮助

     1 # 20190228 hch 辅助分析程序内存泄漏情况的脚本
     2 # 设计思路:首先用ps定期采集所有程序占用内存情况,生成多个文件。然后使用awk分析ps输出的文件,把相同进程占用的内存合并成一行输出
     3 # 计算进程第一次和最后一次出现的时间点占用内存之差,逆序输出即可获得疑似内存泄漏的程序
     4 
     5 # 首先使用以下脚本采集程序每一分钟内存占用信息,采集若干分钟
     6 # while [ 1 ]; do ps -eo 'pid,comm,rsz,vsz,user,comm,args,pcpu,pmem' --sort rsz > ps_info_$(date "+%Y%m%d%H%M%S").txt ; sleep 60; done
     7 
     8 # 使用awk脚本分析内存占用信息 把进程每一个时间点的内存占用情况合并成一行方便对比
     9 # 并且统计进程第一次出现和最后一次出现占用内存差,输出
    10 awk '{
    11     # 每次处理一个新文件时需要特殊处理一下 
    12     if (FNR == 1) {
    13         # 登记文件名称
    14         v_all_file_name = v_all_file_name "," FILENAME;
    15         v_prefix_str = v_prefix_str ",";
    16         ++v_file_cnt;
    17         
    18         # 把上次文件没有出现的pid补上
    19         if (NR != 1) { # FNR == 1 && NR == 1 代表第一个文件
    20             for (v_pid_name in v_mp_pid_cnt) {
    21                 if (v_mp_pid_cnt[v_pid_name] != v_file_cnt - 1) {
    22                     v_mp_pid_rsz[v_pid_name] = v_mp_pid_rsz[v_pid_name] ","
    23                     v_mp_pid_vsz[v_pid_name] = v_mp_pid_vsz[v_pid_name] ","
    24                     v_mp_pid_cnt[v_pid_name] = v_file_cnt - 1;
    25                 }
    26             }
    27         }
    28     }
    29     
    30     v_pid_name = $2 "-" $1 "-" $5; #程序名-进程号-用户名
    31     # 非第一个文件,第一次出现,需要补齐","
    32     if (v_file_cnt != 1 && v_mp_pid_cnt[v_pid_name] == 0) {
    33         v_mp_pid_rsz[v_pid_name] = v_mp_pid_vsz[v_pid_name] = substr(v_prefix_str, 1, v_file_cnt - 1);
    34         v_mp_pid_cnt[v_pid_name] = v_file_cnt - 1;
    35     }
    36     
    37     # rsz是物理内存 单位k
    38     v_mp_pid_rsz[v_pid_name] = v_mp_pid_rsz[v_pid_name] "," $3
    39     # 记录最后值和初始值 方便后面分析(有需要可以改成最大和最小值)
    40     #if ($3 > v_mp_pid_rsz_max[v_pid_name]) v_mp_pid_rsz_max[v_pid_name] = $3;
    41     v_mp_pid_rsz_max[v_pid_name] = $3;
    42     if (v_mp_pid_rsz_min[v_pid_name] == 0 ) # || $3 < v_mp_pid_rsz_min[v_pid_name]
    43         v_mp_pid_rsz_min[v_pid_name] = $3;
    44     
    45     # vsz是虚存 单位k
    46     v_mp_pid_vsz[v_pid_name] = v_mp_pid_vsz[v_pid_name] "," $4
    47     v_mp_pid_vsz_max[v_pid_name] = $4;
    48     if (v_mp_pid_vsz_min[v_pid_name] == 0 ) # || $4 < v_mp_pid_rsz_min[v_pid_name]
    49         v_mp_pid_vsz_min[v_pid_name] = $4;
    50         
    51     # 在本文件出现过就标记一下,后面文件处理完后才知道哪些进程没出现
    52     v_mp_pid_cnt[v_pid_name] = v_file_cnt;
    53 }
    54 END {
    55     print v_all_file_name
    56     for (v_pid_name in v_mp_pid_rsz) {
    57         print "rsz:"v_pid_name, v_mp_pid_rsz_max[v_pid_name] - v_mp_pid_rsz_min[v_pid_name], v_mp_pid_rsz[v_pid_name]
    58         print "vsz:"v_pid_name, v_mp_pid_vsz_max[v_pid_name] - v_mp_pid_vsz_min[v_pid_name], v_mp_pid_vsz[v_pid_name];
    59     }
    60 }' $(ls -rt ps_inf*txt) > ps_trace.txt #按时间逆序分析
    61 
    62 # 将分析结果排序输出
    63 # sh ps_trace.sh; sork -k2n ps_trace.txt | tail -100
  • 相关阅读:
    浮点数越界或者无效1.#IND0
    [转]方差、协方差与相关系数
    『转』 函数、变量命名方法
    感知哈希算法——google用于图片搜索的算法
    C#传值调用与引用调用 解释
    HttpContext.Current.Request.ServerVariab
    怎么去用java经典递归算法?
    泛型的详细解释与示例
    个彻底解释 C#泛型的源代码
    VC++怎么实现Win2000下直接读写磁盘扇区
  • 原文地址:https://www.cnblogs.com/kingstarer/p/10504841.html
Copyright © 2020-2023  润新知