1. 程式人生 > >一次記憶體洩漏問題定位過程與分析

一次記憶體洩漏問題定位過程與分析

現場:

邏輯server伺服器處理能力驟降, 客戶端請求大量失敗.  邏輯server的統計資料顯示,請求量略有增長(客戶端重試的結果), log內容顯示訪問外部介面有一定失敗.

分析:

第一反應是外部介面失敗導致程序處理堵塞,大量請求被堵塞後丟棄導致客戶端重試. vmstat 1看了一下, 發現b欄位保持在20-40之間,wa欄位值遠大於0, 說明程序在等待輸入輸出而被阻塞. 而swap的si欄位值很大,說明系統大量使用交換分割槽,難道有記憶體洩漏? 

procs -----------memory---------- ---swap-- -----io---- -system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
27 21 1052252 5415680   7308  29892    1    1     1     1    0    0  8  8 82  2  0
30 22 1052252 5414936   7308  30276  644    0   644     8 15045 27172  4  3 52 40  0
19 22 1052252 5414440   7328  29640  616    0   620   176 15259 26953  4  3 42 50  0
 3 21 1052252 5413696   7340  30084  572    0   572    32 15385 26809  3  3 45 48  0
 0 21 1052252 5413216   7340  29412  672    0   672     0 15105 26989  4  3 30 63  0

top 看了一下, 發現單個處理程序的RES佔到了300m,而SHR佔用了34m,SHR是程序啟動時分配的共享記憶體,數值是合理的,而RES欄位經驗值是在36m-40m左右,說明有記憶體洩,因此重啟了一下程序進行確認,重啟之後top如下所示:

top - 13:38:13 up 31 days, 22:15,  1 user,  load average: 0.95, 1.57, 1.97
Tasks: 112 total,   8 running, 104 sleeping,   0 stopped,   0 zombie
Cpu(s): 17.9%us,  1.8%sy,  0.0%ni, 79.5%id,  0.0%wa,  0.0%hi,  0.9%si,  0.0%st
Mem:  16429836k total,  2236360k used, 14193476k free,   443520k buffers
Swap:  2104504k total,        0k used,  2104504k free,  1201280k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                               
 8901 root      15   0 74816  38m  34m S    7  0.2   2:38.97  logic_worker                                                      
 9003 root      15   0 74840  38m  34m S    7  0.2   2:39.12  logic_worker

重啟後,發現RES欄位在不斷增長,並且在SHR穩定在34m後還在持續上升,可以確定有記憶體洩漏。

解決:

檢視進段時間的記憶體日報,發現曲線自2.3號釋出新版本後記憶體使用量持續上升,從正常狀態的3.3G,用到了8.7G,到今天記憶體已全部用盡,使用了交換分割槽,才出現異常。

對比當日版本程式碼變更,很快發現在一個外部庫使用中,出現了記憶體洩漏,程式碼大致如下。

char *ptr;

int ret = Encode(ptr, len);

if (ret <0 )

{

    return ErrorEncode;

}

ret = send(ptr, len);

if (ret <0 )

{

    return ErrorSend;

}

return 0;

重新檢視介面描述,發現ptr是在Encode裡被malloc的,需要外部釋放,於是修改程式碼如下:

char *ptr=NULL;

do

{

    int ret = Encode(ptr, len);

    if (ret <0 )

    {

        break;

    }

    ret = send(ptr, len);

    if (ret <0 )

    {

        break;

    }

}

if (NULL != ptr)

{

    free(ptr);

    ptr = NULL;

}

return ret;

修改後釋出,10分鐘後top觀察,發現RES欄位穩定在36m,問題解決。

總結:

1. 在使用外部庫和介面時,一定要弄清楚api的使用方法和注意事宜,另外,在編寫介面時也注意,最好不要在內部申請記憶體,而依賴外部釋放,可以把上面Encode和Send封裝到一個類中,或者一個介面中,介面本身負責編碼和傳送,記憶體申請和釋放,呼叫者只需要知道返回碼即可。

2. 在出現類似運營問題時,注意看系統引數是否異常,如果vmstat看 wa 和 si 值都異常,再綜合top便可以看出,程序異常並不是因為請求量突增,處理時延而導致切換,而是因為記憶體洩漏的原因。

3. 注意對比監控曲線,如網路包量,請求量,記憶體使用,io使用等,通過對比,便可以看出,異常出現的時間點,結合程式碼變更便可定位出原因所在。