最近给系统作了一点优化,前几天去查看系统监控,想看看上线先后cpu使用率曲线变化状况。查看的时候意外发现上线先后内存占用相差很多,20%以上。缓存
原本我没怎么在乎这个问题,由于咱们系统会在运行过程当中缓存部分数据内容。但客户以为有异常,坚持要查。因而把一个月的内存使用状况调出来看,这一看就发现问题了:优化
系统内存占用确实是在缓慢增长,一两天的内存使用率曲线看不出什么,但一个月的能够明显看出来,是一条斜率很小的直线。spa
发现了有内存泄漏,可是想具体分析是哪一个进程泄漏的还真很差办。由于咱们系统有上千个进程在跑,而监控系统又只记录了整体内存占用状况,没记录单个进程内存占用。设计
没有枪,没有炮,只能本身来造 :) 。 我设计了一个简单的分析方法:code
1 首先,我写一个脚本,天天按期记录系统全部进程使用状况,保存到文件,以时间戳命名。(用ps实现)blog
2 脚本跑几天后,我再使用另外一个脚本,把文件合并起来分析,把每一个进程在不一样时间点的内存使用状况合并成一行,逐个进程输出排序
3 比较每一个进程第一次出现与最后一次出现时占用内存之差,按从小到大排序,便可得出可能存在内存泄漏的程序。进程
效果以下:能够看出这个进程内存占用一直在增长,从第一次统计到最后一次统计之间内存使用增长了1460k内存
知道问题后就好办了,使用valgrind+gdb很快就找出致使内存泄漏的代码,纠正便可。class
下面是分析过程当中用到的脚本,但愿对你们有帮助
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