TDengine 研发分享:利用 Windbg 解决内存泄漏问题的实践和经验

news2024/9/22 4:06:58

内存泄漏是一种常见的问题,它会导致程序的内存占用逐渐增加,最终导致系统资源耗尽或程序崩溃。AddressSanitizer (ASan) 和 Valgrind 是很好的内存检测工具,TDengine 的 CI 过程就使用了 ASan 。不过这次内存泄漏问题发生在 Windows 下,我们 CI 暂时还没有覆盖到,因此 TDengine 研发选择使用 Windbg 来解决问题。结果证明,在 Windows 下,使用 Windbg 也是一个不错的选择。

内存泄漏的常用检测方法

内存泄漏通常会发生在以下情况下:

  • 程序未正确释放已分配的内存
  • 程序中存在循环引用,导致垃圾收集器无法回收内存
  • 程序中存在内存泄漏的第三方库或组件

内存泄漏的检测方法主要包括以下几种:

  1. 静态代码分析工具:未释放的指针或内存分配错误等问题,不能检测在程序运行时动态分配内存的情况。
  2. 动态分析工具:可以使用内存分配和释放跟踪器来跟踪程序中的内存分配和释放操作,并检测是否存在内存泄漏的情况。然而,使用某些工具(如Valgrind)可能会对程序的性能产生一定的影响。
  3. 调试器:WinDbg 和 GDB。

优缺点:

  • 静态代码分析工具可以在早期发现问题,但是它们不能检测程序运行时动态分配内存的情况。
  • 动态分析工具可以在程序运行时检测问题,但是它们可能会影响程序性能,并且在检测大型应用程序时可能需要大量的时间和资源。不过在资源充足的测试环境中跑的话,就都不是问题了,比如 ASan 就帮我们发现过不少问题。
  • 调试器可以在程序运行时检测问题,并提供强大的分析工具。

实践分析

基本原理

使用 Windbg 定位内存泄露,依赖 glags 组件记录程序在运行期间所有申请和释放的内存,同时记录的还有申请内存时的调用栈信息。这样在程序运行期间,使用 umdh 组件进行两次快照记录,通过比较两次快照信息的差异,就可以发现两次快照间隔时间段中申请却并未释放的内存申请信息。如果有内存泄露,diff 结果最前边一般就是泄漏点的调用栈信息。当然,两次快照期间,要尽量触发内存泄露,才能更准确的定位。diff 结果中还会有少量正常的申请没来得及释放的调用信息,不过 diff 结果中能看到调用次数,比较容易甄别。

问题介绍

taosdump 在 windows 导入数据出错:

build and install latest TDengine 3.0 branch on Windows
use "taosBenchmark -I stmt -y" to create a lot of tables and data (10000 * 10000).
use "taosdump -D test -o outputFile" to dump out
use "taos -s 'drop database test'" to drop database
use "taosdump -i inputFile" to dump in.

错误日志:taosd “tsem_init failed, errno: 28”

Taosdump: dumpInAvroDataImpl() LN7039 taos_stmt_execute() failed! reason: Out of Memory, timestamp: 1500000009256

定位过程

配置 gflags

gflags 工具应该位于路径:C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags,如果没有的话,可以直接前往 Microsoft 的官方网站下载安装:Windows 调试工具 - Windows drivers | Microsoft Learn

安装完成后,在命令行执行 gflags.exe /i your_application.exe 可设置跟踪目标,同时可以设置相关参数。双击运行也是可以的,Image File 对应 /i 参数,选择启动程序 your_application.exe 后先按 tab 键,然后选择其他配置。

内存泄漏治理实战:TDengine 研发团队使用 Windbg 的经验分享 - TDengine Database 时序数据库

定位步骤

1. 启动 your_application.exe(我要调试的是 taosdump.exe,所以下边是 taosdump.exe)

“C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\gflags” -i taosdump.exe +ust

2. 拷贝 pdb 文件到 mysymbols 目录,pdb 文件存储了编译后的程序的调试信息,和可执行程序一起生成,可以在应用程序生成目录中找到。

3. Set pdb 目录

set _NT_SYMBOL_PATH=c:\mysymbols;srv*c:\mycache*https://msdl.microsoft.com/download/symbols

4. 生成第一次内存快照

"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\umdh" -pn:taosdump.exe -f:C:\xstest\umdhlog\taosdump11.log

5. 生成第二次内存快照

"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\umdh" -pn:taosdump.exe -f:C:\xstest\umdhlog\taosdump12.log

6. 生成快照比较结果(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!dumpInAvroDataImpl+E25 (C:\workroom\TDengine\tools\taos-tools\src\taosdump.c, 6260)
        taosdump!dumpInOneAvroFile+3D2 (C:\workroom\TDengine\tools\taos-tools\src\taosdump.c, 7229)
        taosdump!dumpInAvroWorkThreadFp+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<unsigned int (__cdecl*)(void *),1>+9C (minkernel\crts\ucrt\src\appcrt\startup\thread.cpp, 97)
        KERNEL32!BaseThreadInitThunk+10
        ntdll!RtlUserThreadStart+2B
泄漏点修改

接下来查看代码并修改,C 语言对内存的使用自由度很高,因此也比较麻烦。可以看到有些路径遗漏了 tsem_destory 的调用。

内存泄漏治理实战:TDengine 研发团队使用 Windbg 的经验分享 - TDengine Database 时序数据库

更加详细的代码方案请见 Fix/xsren/td 21762/sem mem leak by facetosea · Pull Request #19580 · taosdata/TDengine · GitHub

总结

工欲善其事必先利其器,掌握更多的工具和手段,在解决问题时才能比较从容,Windbg 定位内存泄漏的方式非常简单,但是很有效。不过需要注意,它依赖 pdb 文件,因此,发布应用程序时要记得保留 pdb 文件。pdb 文件包含了程序的符号信息,能够帮助我们在调试过程中准确定位问题所在。

另外,从出问题的代码可以看出,这块内存的管理方式还是比较容易出错,RAII 机制能较好的避免资源泄露,C 语言中也可以通过模拟 RAII 来达到类似的效果,虽然没有 C++ 那么流畅,也许以后可以考虑优化一下。

RAII(Resource Acquisition Is Initialization)机制是一种重要的资源管理方式,它将资源的获取和对象的生命周期关联起来。通过在对象的构造函数中获取资源,在析构函数中释放资源,我们可以确保资源的正确管理,防止资源泄漏和内存泄漏等问题。RAII 机制在 C++ 等编程语言中得到广泛应用,是一种有效的资源管理方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1481179.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设置matplotlib的style和rcParams

在matplotlib文档中&#xff0c;有3中设置matplot的方法&#xff0c;见文档 https://matplotlib.org/stable/users/explain/customizing.html 目前用得比较多的是设置rcParams&#xff0c;而设置rcParams也有两种方式。下面以 plot的cycler为例进行说明。 cycler就是定义的一些…

13.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-如果没有工具就创造工具

内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容 &#xff1a;12.游戏网络通信存在的问题 现在把游戏网络的架构看了一个小小的大概&#xff0c;可以用它的接口发数据接收数据了&#xff0c;如果真正想用它这一套东西&#xff0c;真正核心不在于它的接口而在于…

私域必备宝藏工具:多微信统一管理聚合聊天

对于私域流量运营者来说&#xff0c;如何高效管理多个微信号成为了一道难题。 不过不用担心&#xff0c;通过微信管理系统&#xff0c;可以实现多个微信同时登录&#xff0c;同一个界面内聚合聊天&#xff0c;省去来回切换账号的步骤。而且&#xff0c;还有很多非常实用且便捷…

Chrome插件 | WEB 网页数据采集和爬虫程序

无边无形的互联网遍地是数据&#xff0c;品类丰富、格式繁多&#xff0c;包罗万象。数据采集&#xff0c;或说抓取&#xff0c;就是把分散各处的内容&#xff0c;通过各种方式汇聚一堂&#xff0c;是个有讲究要思考的体力活。君子爱数&#xff0c;取之有道&#xff0c;得注意遵…

种花

分情况&#xff1a; 第一盆k种选择&#xff0c;之后全部k-1种选择 每次相乘结果对1e97取模 #include <iostream> #include <vector> #include <algorithm> using namespace std; #define endl \n const int N 1e9 7;int main() {ios::sync_with_stdio(f…

camunda7流程版本的控制和使用

1、流程定义的版本控制 从本质上讲&#xff0c;业务流程是长期运行的&#xff0c;流程实例可能会持续数周或数月。同时&#xff0c;流程实例的状态将存储到数据库中。但是&#xff0c;即使仍有正在运行的实例&#xff0c;您也可能需要更改流程定义。 流程引擎支持此功能&…

笔记73:ROS中的各种消息包

参考视频&#xff1a; 33.ROS 的标准消息包 std_msgs_哔哩哔哩_bilibili 34. ROS 中的几何包 geometry_msgs 和 传感器包 sensor_msgs_哔哩哔哩_bilibili 标准消息包&#xff1a;std_msgs常用消息包&#xff1a;common_msgs导航消息包&#xff1a;nav_msgs几何消息包&#xf…

C++数据结构与算法——二叉树的属性

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

C++——String(1)

目录 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 1.2 相关题目 2. 标准库中的string类 2.1 string类&#xff08;了解一下&#xff09; 2.2 string类的常用接口说明 1. string类对象的常见构造 2. string类对象的容量操作 3. string类对象的访问及遍历操作…

瑞_Redis_Redis的Java客户端

文章目录 1 Redis的Java客户端1.1 Jedis快速入门1.1.1 入门案例1.1.1.1 项目构建1.1.1.2 引入依赖1.1.1.3 建立连接1.1.1.4 释放资源1.1.1.5 测试1.1.1.6 完整测试类代码 1.1.2 Jedis连接池1.1.2.1 连接池工具类1.1.2.2 改造原始代码 &#x1f64a; 前言&#xff1a;本文章为瑞…

【Java程序设计】【C00327】基于Springboot的高校教师教研信息填报系统(有论文)

基于Springboot的高校教师教研信息填报系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的高校教师教研信息填报系统&#xff0c;本系统有管理员、教研管理以及教研人员三种角色&#xff1b; 管理员&#xff1a…

bvh文件,人体骨骼重定向

关于两个bvh文件&#xff0c;人体骨骼重定向&#xff0c;小白记录 1、打开 Motionbuilder &#xff0c;选择 打开特定路径下的bvh文件。 绑定骨骼&#xff08;在绑定骨骼过程中&#xff0c;如果骨骼角度&#xff0c;大小之类的不方便&#xff0c;可以shift键加鼠标拖拽界面&…

Fabric V2.5 通用溯源系统——应用后端GIN框架部分设计

本节对Fabric V2.5 通用溯源系统的应用后端部分做一个简单的介绍,包括目录结构、文件作用、用户注册登录与农产品信息上链过程介绍。此节内容免费发布在TrueTechLabs Fabric学习交流QQ群。 购买专栏前请认真阅读:《Fabric项目学习笔记》专栏介绍 TrueTechLabs Fabric学习交流…

蓝桥杯刷题2

1. 修建灌木 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int n scan.nextInt();for (int i 1;i < n1;i){int distance Math.max(i-1,n-i);System.out.println(distance*2);}scan.close…

Node.js+Express后端,自定义接口

6分钟学会Express 后端 API 开发 Node.js 2020最新版_哔哩哔哩_bilibili 要使用Node.js和Express搭建一个简单的后台服务器&#xff0c;用于接收带有token的请求头&#xff0c;你可以按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保你已经安装了Node.js和npm&#xff0…

马斯克正式起诉OpenAI和奥特曼!

就在刚刚&#xff0c;马斯克闹出来一件大事——正式起诉OpenAI和Sam Altman&#xff0c;并要求OpenAI 恢复开源GPT-4等模型&#xff01; 众所周知&#xff0c;马斯克这两年一只在推特上指责 OpenAI是CloseAI(不开源)&#xff0c;但都只是停留在口头上。 而这次马斯克动了真格。…

Qt 自定义长条进度条(类似播放器进度条)

1.运行界面 2.步骤 其实很简单。 2.1绘制底图圆角矩形 2.2绘制播放进度圆角矩形 参考&#xff1a;painter绘图 3.源码 #pragma once#include <QWidget> #include <QLabel> #include <QHBoxLayout> #include <QMouseEvent> #include <QDebug&g…

2D割草/吸血鬼游戏 性能优化——GPU Spine动画

视频中万人同屏方案(gpu动画、渲染、索敌、避障等功能)&#xff0c;可某宝搜店铺&#xff1a;【游戏开发资源商店】获取整套方案源码。 在过去的几年里&#xff0c;割草、类吸血鬼玩法的游戏频出爆款&#xff0c;其丰富的技能、满屏特效、刷怪清屏的解压畅快是此类游戏的核心&…

队列实现栈与栈实现队列

文章目录 前言一、使用队列实现栈二、使用栈实现队列 前言 1、用于巩固栈和队列。 2、本章是使用纯C语言实现的栈和队列&#xff0c;不懂的可以先看看这个喔&#xff1a;c语言实现栈和队列&#xff0c;当然这里直接用C的栈和队列会更方便哦。 3、利于复习C语言的知识点。 一、使…

杭电OJ 2044 一只小蜜蜂... C++

思路&#xff1a;本质与斐波那契数列一样&#xff0c;关键在于a与b之差 #include <iostream> #include <vector> using namespace std; int main() { vector<long long> nums(51); nums[0] 0; nums[1] 1; nums[2] 2; for (int i 3; i < 5…