記憶體洩漏是乙個常見的問題,它會導致程式的記憶體占用逐漸增加,最終導致系統資源耗盡或程式崩潰。 AddressSanitizer (ASAN) 和 Valgrind 是很好的記憶體檢測工具,TDengine 的 CI 程序使用 ASAN。 不過,這次記憶體洩漏問題發生在Windows下,我們的CI還沒有覆蓋,所以TDENGINE研發選擇使用WindBG來解決這個問題。 事實證明,在Windows下,使用WindBG也是乙個不錯的選擇。
記憶體洩漏通常發生在以下情況下:
程式未正確釋放分配的記憶體。
程式中存在迴圈引用,這些迴圈引用會導致垃圾回收器記憶體失敗。
程式中存在記憶體洩漏的第三方庫或元件。
記憶體洩漏的主要檢測方法如下:
靜態分析工具:程式執行時未檢測到記憶體動態分配的指標或記憶體分配錯誤等問題。
動態分析工具:您可以使用記憶體分配和釋放***來跟蹤程式中的記憶體分配和釋放操作,並檢測是否存在記憶體洩漏。 但是,使用某些工具(例如 Valgrind)可能會對程式的效能產生一些影響。
偵錯程式:windbg 和 gdb。
優點和缺點:
靜態分析工具可以及早發現問題,但它們無法檢測程式執行時記憶體的動態分配方式。
動態分析工具可以在程式執行時檢測問題,但它們會影響程式效能,並且在檢測大型應用程式時可能需要大量時間和資源。 但是,在資源充足的測試環境中執行不是問題,例如,ASAN 幫助我們發現了很多問題。
偵錯程式可以在程式執行時檢測問題,並提供強大的分析工具。
使用 windbg 定位記憶體洩漏,依靠 GLAGS 元件來記錄程式在執行時請求和釋放的所有記憶體,以及請求記憶體時的呼叫堆疊資訊。 這樣,在程式執行過程中,umdh元件用於記錄兩個快照,通過比較兩個快照之間的差異,可以找出在兩個快照之間的時間間隔內應用但未釋放的記憶體應用資訊。 如果存在記憶體洩漏,則差異結果的第一條邊通常是洩漏點處的呼叫堆疊資訊。 當然,在兩次快照期間,應該盡可能地觸發記憶體洩漏,以便更準確地定位它。 diff 結果中也會有少量的呼叫訊息,正常應用還沒來得及發布,但是在 diff 結果中可以看到呼叫的數量,更容易識別。
在 Windows 中匯入資料時出現 taosdump 錯誤:
build and install latest tdengine 3.0 branch on windowsuse "taosbenchmark -i stmt -y" to create a lot of tables and data (10000 * 10000).use "taosdump -d test -o outputfile" to dump outuse "taos -s 'drop database test'" to drop databaseuse "taosdump -i inputfile" to dump in.錯誤日誌:taosd “tsem init failed, errno: 28”。
taosdump: dumpin**rodataimpl() ln7039 taos_stmt_execute() failed! reason: out of memory, timestamp: 1500000009256
Gflags 工具應位於以下路徑: C:程式檔案 (x86) Windows Kits 10 偵錯程式 x64 Gflags,如果沒有,可以直接進入 Microsoft 的官方 *** 安裝:
安裝完成後,執行 gflags。 命令列exe /i your_application.exe 可以設定跟蹤目標並設定相關引數。 也可以雙擊執行,影象檔案對應 i 引數,然後選擇您的應用程式啟動器exe,然後選擇其他配置。
1.啟動應用程式exe (我正在除錯 taosdumpexe,所以底部是 taosdumpexe)
c:\program files (x86)\windows kits\10\debuggers\x64\gflags” -i taosdump.exe +ust
2.將 pdb 檔案複製到 mysymbols 目錄下,pdb 檔案儲存了編譯程式的除錯資訊,這些資訊是用可執行程式生成的,可以在應用生成目錄中找到。
3.設定 pdb 目錄。
set _nt_symbol_path=c:\mysymbols;srv*c:\mycache*4.拍攝第乙個記憶體快照。
"c:\program files (x86)\windows kits\10\debuggers\x64\umdh" -pn:taosdump.exe -f:c:\xstest\umdhlog\taosdump11.log5.拍攝第二個記憶體快照。
"c:\program files (x86)\windows kits\10\debuggers\x64\umdh" -pn:taosdump.exe -f:c:\xstest\umdhlog\taosdump12.log6.生成快照比較結果 (UMDH)。
"c:\program files (x86)\windows kits\10\debuggers\x64\umdh" c:\xstest\umdhlog\taosdump11.log c:\xstest\umdhlog\taosdump12.log -f:c:\xstest\umdhlog\taosdumpdiff11_12.log由於 taosdump 程式從開始到退出都在執行大量業務工作,因此快照之間很容易發生記憶體洩漏。 988040 6ecf0 表示“應用程式數量發布數量”,很明顯發生了記憶體洩漏,洩漏點在 buildrequest 函式的 SEM init 中。
+ 919350 ( 988040 - 6ecf0) 201b0 allocs backtrace9cb6973f+ 1ea5c ( 201b0 - 1754) backtrace9cb6973f allocations ntdll!rtlpallocateheapinternal+948d5 taos!heap_alloc_dbg_internal+1f6 (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 359) taos!heap_alloc_dbg+4d (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 450) taos!_calloc_dbg+6c (minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp, 518) taos!calloc+2e (minkernel\crts\ucrt\src\appcrt\heap\calloc.cpp, 30) taos!sem_init+5d (c:\workroom\tdengine\contrib\pthread\sem_init.c, 109) taos!buildrequest+209 (c:\workroom\tdengine\source\client\src\clientimpl.c, 192) taos!stmtcreaterequest+73 (c:\workroom\tdengine\source\client\src\clientstmt.c, 15) taos!stmtsettbname+115 (c:\workroom\tdengine\source\client\src\clientstmt.c, 588) taos!taos_stmt_set_tbname+7f (c:\workroom\tdengine\source\client\src\clientmain.c, 1350) taosdump!dumpin**rodataimpl+e25 (c:\workroom\tdengine\tools\taos-tools\src\taosdump.c, 6260) taosdump!dumpinone**rofile+3d2 (c:\workroom\tdengine\tools\taos-tools\src\taosdump.c, 7229) taosdump!dumpin**roworkthreadfp+20b (c:\workroom\tdengine\tools\taos-tools\src\taosdump.c, 7306) taosdump!ptw32_threadstart+cd (c:\workroom\tdengine\contrib\pthread\ptw32_threadstart.c, 233) taosdump!thread_start+9c (minkernel\crts\ucrt\src\appcrt\startup\thread.cpp, 97) kernel32!basethreadinitthunk+10 ntdll!rtluserthreadstart+2b接下來勾選**並修改,C語言使用記憶體的自由度很高,所以也比較麻煩。 您可以看到某些路徑錯過了對 tsem destory 的呼叫。
欲瞭解更多詳情,請看這裡
要想做好,首先要磨礪你的工具,掌握更多的工具和手段,更冷靜地解決問題,windbg定位記憶體洩漏的方法很簡單,但非常有效。 但是,請務必注意,它依賴於 pdb 檔案,因此請記住在發布應用程式時保留 pdb 檔案。 pdb 檔案包含有關程式的符號資訊,這有助於我們在除錯過程中查明問題。
另外,從**問題可以看出,這個記憶體的管理還是比較容易出錯的,RAII機制可以更好的避免資源洩漏,C語言也可以通過模擬RAII達到類似的效果,雖然不如C++流暢,但也許以後可以考慮優化一下。
RAII(Resource Acquisition is Initialization)機制是一種重要的資源管理方法,它將資源的獲取與物件的生命週期相關聯。 通過在物件的建構函式中獲取資源並在析構函式中釋放資源,我們可以確保資源的正確管理,並防止資源洩漏和記憶體洩漏等問題。 RAII機制廣泛應用於C++等程式語言中,是管理資源的有效方式。