介紹使用腳本判斷內存泄漏的簡便方法
Document #: 2811995H29001
Body:
[標題]
介紹使用腳本判斷內存泄漏的簡便方法
內容提要:
本文份四個部分介紹如何使用提供的腳本定位分析應用內存泄漏
一,前言
二,內存使用介紹
三,腳本使用介紹
四,示例分析
五,總結
說明:
介紹使用腳本判斷內存泄漏的簡便方法
一,前言
應用內存泄漏是UNIX 系統中比較常見的一種現象,如何定位並確定內存泄漏的
應用是一個相對復雜的過程。本文將探討內存泄漏的產生,及在AIX 系統中如何利用
提供的腳本,在系統級層面判斷內存泄漏的進程。
二,內存使用介紹
C 語言中,應用使用malloc() 函數從進程的堆棧中申請內存,而在C++ 中內存的申請
使用是在類的初始化中通過構造函數建立的,包含類及其成員。當進程不再需要部
分內存是,將通過系統free() 函數歸還給操作系統,而在C++ 中則通過析構函數釋放
類的內存。進程終止時,進程的使用的棧的內存將全部歸還給操作系統。如果進程
申請使用的內存不能釋放,而且進程堆棧持續增長,應用可能存在內存泄漏。最終
的結果是進程消耗所有的系統內存,導致應用異常終止,或者是系統啟動自身的保
護機制殺掉進程,進而釋放系統的內存。
操作系統開發者不斷的探求如何更有效的使用、分配內存的策略,在AIX 4.2 –
5.2 版本中,使用Yorktown 機制分配系統內存,相對於以前的版本,AIX 5.3 中引入
了新的內存分配使用機制watson ,基於rbtree(red-black tree) 結構,提高了malloc 的
性能,同時減少了堆棧的碎片。
三,腳本使用介紹
本文提供一組腳本,通過這組腳本,可以比較簡單、快速的判斷定位,哪一個進程
產生內存泄漏。腳本名稱post_vg.sh ,使用方法如下:
1) 首先搜集系統的初始狀態,包含每個進程使用的內存情況:
# ps vg > ps.before
2) 間隔一段合適的時間后,如15 分鍾,根據應用或系統的情況而定,重新收集系統的狀態
# ps vg > ps.after
3) 比較系統的前后狀態,判斷進程的內存使用情況,輸出信息中Delta 表明進程內存的前后
使用情況,如果值增加,其中一個可能的情況就是內存泄漏
# ./post_vg ps.before ps.after
腳本的內容如下:
#!/bin/ksh
#
#
# Correlate ps.before and ps.after data ..
#
# command output from ps vg
#
ONE_FILE=temp_ps_vg
print_help() {
print "Version 1.0"
print "Usage: post_vg.sh [single_file|before_ps after_ps]"
print " Post process ps vg output "
print " "
print " where, "
print " single_file contains a before and after snapshot"
print " "
print " No files specified - assume"
print " ==> ps_vg.before "
print " ==> ps_vg.after "
exit -1
}
main() {
if [[ $1 == "-?" ]]
then
print_help
exit -1
fi
if [[ $# == 2 ]]
then
cat $1 $2 > $ONE_FILE
elif [[ $# == 1 ]]
then
cat $1 > $ONE_FILE
else
cat ps_vg.before ps_vg.after > $ONE_FILE
fi
post_vg
rm $ONE_FILE
}
post_vg() {
cat $ONE_FILE | awk 'BEGIN {
list_label = "None"
}
/PID/ {
if( list_label == "None" )
list_label = "Before"
else
list_label = "After"
next
}
{
pid_list[$1]
pid_size[$1, list_label ] = $6
pid_name[$1] = $13
}
END {
printf("%15s\t%10s\t%11s\t%10s\t%10s\n", "pid", \
"Name", \
"Before Size", \
"After Size", \
"Delta")
for( pid in pid_list ) {
if( (pid,"Before") in pid_size && (pid,"After") in pid_size ) {
delta = pid_size[pid, "After"] - pid_size[pid, "Before"]
d_total += delta
printf("%15s\t%10s\t%11d\t%10d\t%10d\n", \
pid, \
pid_name[pid], \
pid_size[pid, "Before"], \
pid_size[pid, "After"], \
delta )
}
}
printf("*** Total Delta %d\n", d_total)
}'
}
main $@
四,示例分析
通過以下的簡單應用,借助以上的腳本,分析系統內存的使用。
測使用例:
#include <stdio.h>
main() {
char *ptr;
int count=0;
/* 申請系統內存 */
for (count=0; count<100000; count++) {
ptr = (char *)malloc(1024*1024);
memset(ptr, 0, 1024*1024);
/* 釋放系統內存,決定應用是否產生內存泄漏。如果生成內存泄漏的應用,需要打開注釋 */
/* free(ptr);
*/
sleep(1);
}
}
編譯鏈接以上程序,生成有內存泄漏和沒有內存泄漏兩種情況,應用名稱如下:
memleak : 有內存泄漏
nmemleak :沒有內存泄漏
測試如下:
1) 測試應用沒有內存泄漏
a) 運行nmemleak程序
# nmemleak
b) 使用命令ps收集系統的狀態
# ps vg >ps.before
c) nmemleak運行一段時間后,假定30秒,重新收集系統的狀態
# ps vg >ps.after
d) 使用post_vg.sh分析狀態文件,並查看Delta項是否為0
# ./post_vg.sh ps.before ps.after
輸出結果如下:
pid Name Before Size After Size Delta
... ...
284 wait 40 40 0
192946 /usr/sbi 1300 1300 0
168240 nmemleak 1120 1120 0
119182 /usr/sbi 772 772 0
57792 /usr/sbi 640 640 0
... ...
*** Total Delta 0
從上面的分析結果可以看出,Delta項值為0,也就是說,可以基本斷定nmemleak沒有內存泄漏。
2) 測試應用有內存泄漏
a) 運行memleak程序
# memleak
b) 使用ps命令收集系統的初始狀態
# ps vg >ps.before
c) memleak運行一段時間后,假定30秒,重新運行ps命令收集系統的狀態
# ps vg >ps.after
d) 使用post_vg.sh分析狀態文件,查看Delta項
# post_vg.sh ps.before ps.after
查卡分析結果如下:
pid Name Before Size After Size Delta
... ...
90456 /usr/ccs 128 128 0
110734 /usr/sbi 1520 1520 0
209368 telnetd 624 624 0
168274 memleak 13408 42080 28672
147862 /usr/sbi 212 212 0
36882 pilegc 144 144 0
115076 /usr/sbi 1040 1040 0
78120 rpc.lockd 204 204 0
284 wait 40 40 0
... ...
*** Total Delta 28672
分析以上結果,可以看到,memleak 應用使用的進程內存增加了28672K 字節,明顯存在內存
泄漏。接下來,要做的是定位應用那個函數造成的內存泄漏,需要按行定位代碼,或者使用
第三方的內存檢測工具,如Purify 。
五,總結
決定於應用的復雜程度,確定和定位內存泄漏,難度會相應的不同,結合系統提供的其他工具,
如svmon ,和更專業的內存使用分析工具判斷應用內存的使用情況。