内存泄漏是一个常见的问题,它会导致程序的内存占用逐渐增加,最终导致系统资源耗尽或程序崩溃。 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++等编程语言中,是管理资源的有效方式。