1. 程式人生 > >線上伺服器指令碼記憶體洩漏問題排查

線上伺服器指令碼記憶體洩漏問題排查

前一陣子在做對輸入法移動端伺服器失敗率監控的過程中使用C++編寫了一個指令碼。由於監控指令碼就是一個死迴圈,希望可以長時間執行。但是沒執行2天多,監控程式就失效了。到虛擬機器上一看原來是監控指令碼已經在虛擬機器上崩潰,通過分析崩潰檔案大致原因是因為記憶體洩漏的問題。然後觀察機器的執行過程發現指令碼每次迴圈的時候都會有大搞90kb的記憶體洩漏。

檢視監控程式的原始碼:

啥也沒有啊?就是建立了一個執行緒,幹完活後釋放掉了。為什麼會有這麼大的記憶體洩漏呢?

初步懷疑是createThread的問題,查了一下輸入法的程式碼大量使createThread的情況,難道是它,不會吧!

在網上搜了一下createThread記憶體洩漏,果然存在這種情況。記憶體洩露存在的原因,是由CreateThead建立的執行緒不會去檢查CRT的TLS資料是否需要釋放。

在瞭解了CreateThread導致記憶體洩露的原因後,我簡單考慮了一下避免這種記憶體洩露的方法。

首先老老實實的使用_beginthead(ex)函式,是最穩妥的辦法。

其次能否避免使用CRT中依賴TLS的函式呢?

也許可以,但是我們所書寫的程式碼,不完全是我們自己在用,而且我們CreateThead所建立的執行緒,也不一定跑的都是自己的程式碼,例如我們提供一個庫給別人使用,難道還要特別說明不允許使用errno/localtime等函式麼?因此這個方法是不建議的。

如果我想用CreateProcess,或者我所使用的底層庫使用的是CreateProcess函式,我又不可避免的會使用依賴於TLS的VC執行時庫函式,有什麼辦法能保證ptd會被釋放呢?

我們可以自己釋放ptd。前面的分析可以看出,_endthread()函式呼叫了_freeptd(ptd)來釋放ptd,因此我們可以線上程函式的末 尾顯示的呼叫_endthread()或_endthreadex(retcode)函式來釋放ptd。在查看了_freeptd函式的程式碼後,我發現如 果傳入引數是NULL,_freeptd函式釋放的就是caller執行緒的ptd,因此也可以直接呼叫_freeptd來執行清理。

另外,我們也可以自動釋放ptd。在VC的工程屬性中,可以選擇執行時庫的型別,如圖:


如果我們選擇/MTd或/MDd,執行時庫以動態方式連結,即我們的程式會使用傳說中的msvcrt*.dll。在這個dll的入口函式中,會在 DLL_THREAD_ATTACH時為attach的執行緒初始化TLS資料,在DLL_THREAD_DETACH時為detach的執行緒呼叫 _freeptd函式執行清理。因此如果我們使用VC的動態庫,使用CreateThread和使用_beginthread是同樣安全的。

總結一下,避免CreateThread引發洩露,大致有幾種方法:

1. 使用_beginthread/_beginthreadex函式建立執行緒

2. 線上程函式return前,顯示呼叫_endthread/_endthreadex函式

3. 線上程函式return前,顯示呼叫_freeptd(NULL),此方法在C語言中有效

4. 使用/MTd或/MDd引數

原文連結

如需轉載該篇文章,請註明來自“搜狗測試”