1. 程式人生 > >分享一個輔助分析內存泄漏的腳本

分享一個輔助分析內存泄漏的腳本

|| args bsp all 簡單的 系統內存 span 單位 占用內存

  最近給系統做了一點優化,前幾天去查看系統監控,想看看上線前後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

分享一個輔助分析內存泄漏的腳本