引发C++程序内存泄漏的原因分析与排查方法总结

目录

1、概述

2、内存泄漏与程序的位数

3、调用哪些接口去动态申请内存?

4、引发内存泄漏的常见原因总结

4.1、通过malloc/new等动态申请的内存,在使用完后,没有调用free/delete去释放(也可能是调用了上面讲到的HeapAlloc或VirtualAlloc等API接口)

4.2、函数调用者调用内部申请内存的接口,函数调用完成后,没有去释放接口内部动态申请的内存

4.3、在多态中,没有将基类的析构函数声明为虚函数,导致delete多态时的子类对象时没有执行子类的析构函数,导致内存泄漏

4.4、第三方注入库有内存泄漏,注入到我们的程序进程中导致我们的程序发生内存泄漏

5、内存泄漏问题的排查

5.1、发生内存泄漏的程序为何会发生闪退崩溃?       

5.2、如何确定当前程序发生了内存泄漏?       

5.3、使用工具排查内存泄漏    

6、最后


C++软件异常排查从入门到精通系列教程(核心精品专栏,订阅量已达600多个,欢迎订阅,持续更新...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/125529931C/C++实战专栏(重点专栏,专栏文章已更新480多篇,订阅量已达数百个,欢迎订阅,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/140824370C++ 软件开发从入门到实战(重点专栏,专栏文章已更新280多篇,欢迎订阅,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_2276111.html       内存泄漏是C++程序常见内存问题之一,在软件项目中时常会遇到。内存泄漏问题排查起来可能会比较麻烦,有时需要尝试多种方法和工具,特别是大型软件项目,排查的难度会大些。本文根据多年的项目实践以及遇到的多个内存泄漏的问题案例及场景,对引发内存泄漏的原因以及排查方法做一个详细系统的总结,以供大家借鉴或参考。

1、概述

       ​所谓内存泄漏,就是动态申请的内存在使用完后没有释放,导致了泄漏。有内存泄漏的代码如果被频繁地执行,则会导致频繁的内存泄漏,会导致程序占用的虚拟内存越来越多,程序运行可能会变慢(程序要频繁地在虚拟内存与物理内存之间切换)。当程序占用的虚拟内存接近或达到程序虚拟内存的上限时,就会因内存不足产生异常,程序紧接着就会发生崩溃或闪退。

一般程序的业务代码基本都是运行在用户态的,占用的主要是用户态的虚拟内存,所以内存泄漏一般都是用户态的内存泄漏。如果是驱动程序,则运行在内核态的。

       程序中发生内存泄漏,且内存泄漏在持续的发生(发生内存泄漏的代码在频繁地执行),就会因内存不足产生异常,程序紧接着就会发生崩溃或闪退。这种内存泄漏是致命的,也是必须要解决的! 

       此外,内存泄漏具有一定的隐蔽性,直到程序发生闪退或崩溃时才能被发现。程序刚启动时可以正常运行,可能运行一段时间后程序就莫名其妙的闪退崩溃了。可能内存泄漏发生在某个代码块中,当执行该代码块的相关操作或者业务流程时才会触发内存泄漏,持续地执行该操作,才会导致程序的内存持续的增长。

2、内存泄漏与程序的位数

​        根据内存寻址的位数,程序一般分为32位和64位。64位程序是不能运行在32位系统中的,但32位程序是可以运行在64位系统中的(64位系统对32位程序保持兼容)。关于32位程序与64位程序的区别以及相关问题案例,不是此处讨论的重点,我就不再展开了,可以查看我之前写的文章:

32位程序在64位系统中运行需要注意的重定向问题icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/139564448应用程序无法正常启动(0xc000007b)问题的详细排查(文章第8节详细讲到32位程序与64位程序的区别)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/126298265       对于32位程序,系统在程序启动时会给程序进程分配4GB的虚拟内存(内存按32位寻址),在默认情况下,2GB是用户态的虚拟内存,2GB是内核态的虚拟内存。如果程序中内存泄漏的代码(用户态的泄漏)被频繁地执行,程序占用的用户态虚拟内存可能很快就能达到2GB的用户态虚拟内存的上限,从而会因为内存不足导致程序发生异常。当然,我们可以在Visual Studio中对exe主程序工程开启/LargeAddressAware大地址编译选项

​将32程序用户态虚拟内存从2GB扩大到3GB,虽然可以缓解内存泄漏的问题,但对于持续的泄漏也无济于事。

       对于64位程序,内存寻址范围是64位,系统会分配比32位程序的4GB多的多的虚拟内存,但即便再大,也是有上限的,如果持续泄漏,也总会到耗尽的那一刻的,程序终归会因为内存不足发生崩溃闪退。

至于内存不足后程序为什么会发生崩溃或闪退,下面会详细讲解,此处就不展开了。

3、调用哪些接口去动态申请内存?

​       C++代码中主要使用操作符new(要用delete去释放)或者C函数malloc(要用free去释放)去动态申请内存。在Windows C++程序中,也可以调用Windows系统API函数HeapAlloc从堆上分配内存(要用HeapFree去释放),还可能调用API函数VirtualAllocVirtualAllocEx从虚拟内存上分配内存(要用VirtualFreeVirtualFreeEx去释放),还可能调用其他的API函数。

       调用这些操作符或函数去动态申请内存时,如果在内存使用完后不去调用对应的接口去释放内存,则会导致内存泄漏。注意,使用不同操作符或者函数去动态申请内存,要用对应的操作符或者函数去释放,不能交叉使用,胡乱调用接口可能会引发异常崩溃。

有时我们使用new或malloc去申请一段较大的连续内存时,可能会因为内存紧张而申请不到,而使用VirtualAlloc或VirtualAllocEx申请到的几率要大一些,这点我们在项目中使用过。

       之前排查的一个WebRTC开源库中的内存泄漏问题,就是没调用HeapFree去释放使用HeapAlloc申请来的堆内存,导致的内存泄漏可以去查看对应的文章:

开源WebRTC库放大器模式在采集桌面图像时遇到的DPI缩放与内存泄漏问题排查icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131146506       有一个典型的C++面试题,问new/delete与malloc/free的区别,可以查看我的文章:

【C++经典面试题】C++中已经有了malloc/free,为什么还要new/delete?icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/140571083


       在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:该精品技术专栏的订阅量已达到580多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章已经更新到200篇以上,持续更新中!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达220多个,专栏文章已经更新到480多篇,持续更新中!欢迎订阅!)

C/C++实战进阶(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。

专栏3:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: (本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,专栏文章已经更新到280多篇,持续更新中!欢迎订阅!)

C++ 软件开发从入门到实战(专栏文章,持续更新中...)icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


4、引发内存泄漏的常见原因总结

​       根据多年的项目实践以及遇到的多个内存泄漏的问题案例及场景,总结出引发内存泄漏的常见原因主要有以下几种。

4.1、通过malloc/new等动态申请的内存,在使用完后,没有调用free/delete去释放(也可能是调用了上面讲到的HeapAlloc或VirtualAlloc等API接口)

       可能是新人写的代码,因为考虑问题不全面,压根就没想去释放内存。再者,可能是业务逻辑控制的有问题导致的,动态申请的内存是给软件业务模块用的,受业务逻辑控制的,使用完成后应该要将内存释放掉,释放的时机也是根据业务要求去控制的。如果控制的不好,就执行不到释放内存的代码。也有可能是代码业务逻辑有调整,因为有部分代码点没有同步修改,导致内存没有释放,比如我前段时间排查的内存泄漏:

使用历史版本比对法排查C++程序中的内存泄漏问题icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/141002375

4.2、函数调用者调用内部申请内存的接口,函数调用完成后,没有去释放接口内部动态申请的内存

       调用某些特殊的接口,接口内部动态申请内存,然后将地址返回给接口调用者使用,调用者在使用完后需要将对应的内存释放掉,如果没释放,则会造成内存泄漏。

       比如我们在调用GDI+库中的Bitmap静态成员函数FromFile、FromStream和FromHBITMAP等静态接口时,这些接口内部都会new出一个Bitmap对象,然后把new出的Bitmap对象地址作为返回值,返给外部使用,如下所示:

// 头文件中函数的声明
static Bitmap* FromFile(IN const WCHAR *filename,IN BOOL useEmbeddedColorManagement = FALSE
);static Bitmap* FromStream(IN IStream *stream,IN BOOL useEmbeddedColorManagement = FALSE
);static Bitmap* FromHBITMAP(IN HBITMAP hbm,IN HPALETTE hpal);
// cpp文件中函数的实现
inline Bitmap* 
Bitmap::FromFile(IN const WCHAR *filename,IN BOOL useEmbeddedColorManagement)
{return new Bitmap(filename, useEmbeddedColorManagement);
}inline Bitmap* 
Bitmap::FromStream(IN IStream *stream,IN BOOL useEmbeddedColorManagement)
{return new Bitmap(stream, useEmbeddedColorManagement);
}inline Bitmap* 
Bitmap::FromHBITMAP(IN HBITMAP hbm, IN HPALETTE hpal)
{return new Bitmap(hbm, hpal);
}

       上述函数不会去管理这个返回的Bitmap对象的生命周期(从对象创建到对象销毁),因为是给外部使用的,外部根据自己的需要确定使用到什么时候,并且外部负责在不用的使用去delete这个对象,如下所示:

Bitmap *pSrcBmp = Bitmap::FromHBITMAP(hBmp, NULL);
if (pSrcBmp == NULL)
{return;
}// ... 中间代码省略delete pSrcBmp;

如果不去delete,则会导致内存泄漏。

4.3、在多态中,没有将基类的析构函数声明为虚函数,导致delete多态时的子类对象时没有执行子类的析构函数,导致内存泄漏

       这个场景有较强的隐蔽性在多态中,将子类对象地址赋值给父类的指针,我们在delete该父类指针时,因为指针中存放的是子类的对象,应该要执行子类的析构函数,析构函数除了释放子类对象本身的内存,可能在析构中还会执行其他资源的释放,如果父类的析构函数不声明为虚函数,则这种场景下子类的析构就不会被执行到,就会导致内存泄漏。这个场景的示例代码如下:

// 基类
class CBase
{
public:CBase();~CBase();
};// 派生类
class CDerived
{
public:CDerived();~CDerived();
};// 多态
CBase* pBase = new CDerived;
delete pBase;

new出一个子类CDerived对象,赋值给父类的指针CBase* pBase,然后再delete pBase时因为父类CBase的析构没有声明为虚函数,导致没有执行子类CDerived的析构函数,从而导致内存泄漏。

       有时,子类的析构函数不仅要析构子类对象的内存,可能还会在析构函数中去释放其他的资源,比如我们在子类对象中定义了一个STL列表vector<CChatDlg*> m_vtChatDlgList,在子类的析构函数中会去for循环遍历这个列表去delete列表中的CChatDlg对象,如下所示:

class CDerived
{
public:CDerived();~CDerived();private:vector<CChatDlg*> m_vtChatDlgList;
};CDerived::~CDerived()
{for ( int i = 0; i < m_vtChatDlgList.size(); i++ ){CChatDlg* pChatDlg = m_vtChatDlgList[i];delete pChatDlg;}
}

       如果子类的析构函数没执行到,则不仅子类对象本身的内存没释放,而且析构函数中释放STL列表中存放的类对象也没有释放,这些都是内存泄漏。

到底什么时候该将析构函数声明为虚函数?什么时候不要将析构函数声明为虚函数呢?

如果将C++类的析构函数声明为虚函数,C++类对象中会内置一个虚函数表指针,还会维护一个虚函数表(存放虚函数代码段的地址),这是声明为虚函数的开销。如果类可能会被继承,则最好将其析构函数声明为虚函数。如果类不会被继承,且明确使用了关键字final标识(使用该关键字就设定该类不能被继承,这是C++11新增的关键字),就不要将析构函数设置为虚函数。

关于C++11新增的几个常用关键字的说明,可以查看我的文章:

C++11 新特性 ① | C++11 常用关键字实战详解icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/132701306

4.4、第三方注入库有内存泄漏,注入到我们的程序进程中导致我们的程序发生内存泄漏

       这是我们在项目中遇到的真实案例,客户系统中安装了第三方安全软件,安全软件基本都是通过远程注入到各个进程的方式实现对各个进程实时监控。结果这个注入模块在某个场景下有内存泄漏,因为其注入到我们的程序进程中,位于我们的进程空间中,占用的是我们程序进程的虚拟内存,这个注入模块有泄漏,就是我们整个进程有泄漏,对我们的程序产生直接的影响。

       当时我们的软件在客户的机器上运行半个小时后,就会出现闪退崩溃,后来我们发现程序的内存在运行过程中在不断的增长,判断是程序中发生了内存泄漏。客户一开始不认为是第三方安全软件的问题,因为其他软件在该问题电脑上运行并没有明显的问题。后来我们使用内存分析工具定位到泄漏发生在第三方安全软件的注入模块中,客户才愿意联系第三方安全软件去排查这个泄漏问题。

5、内存泄漏问题的排查

        首先我们要确定程序中发生了内存泄漏,然后使用一些分析工具去分析,找出内存泄漏的代码点,进而解决问题。

5.1、发生内存泄漏的程序为何会发生闪退崩溃?       

       往往在程序运行发生异常后,经过观察分析才会发现程序中发生了内存泄漏,是内存泄漏引发的问题。

程序运行过程中遇到的各种问题,基本都是后知后觉,都是产生了异常的现象后,才知道程序发生了异常,才去排查引发异常的原因!内存泄漏问题也是如此,也是后知后觉,通过现象推测分析程序中可能发生了内存泄漏。

       程序中发生持续的内存泄漏,程序的用户态虚拟内存就会逐渐地接近系统给程序进程分配的用户态虚拟内存的上限(比如32位程序,默认的用户态虚拟内存的上限是2GB),此时再使用malloc或new去动态申请内存时可能就会申请失败,导致程序产生异常,我们在项目中已多次遇到过。

​        我们在项目中主要遇到以下两类场景:

1)内存不足,malloc申请内存失败,返回NULL

       这个场景发生在WebRTC开源库中,开源库内部因为业务的需要,调用malloc动态申请内存。因为程序占用的虚拟内存接近用户态的虚拟内存的上限,没有多余的内存可供分配了,所以malloc申请内存失败,返回NULL。

       WebRTC开源库内部的代码判断出malloc返回NULL,认为这个是fatal致命的(因为申请不到内存,业务没法正常的展开,WebRTC内部认为这是致命的),直接调用abort强行将当前进程终止了!其实此时并没有产生异常,是调用abort强行终止进程的,程序直接闪退消失了,给人感觉是程序崩溃了!这个项目问题案例,我之前专门写了文章,可以去查看文章:

WebRTC开源库内部调用abort函数引发C++程序发生闪退问题的详细排查icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/129460580       对于这种调用abort强行终止进程的问题,还有一个细节值得注意一下。如果程序中安装了异常捕获,此种场景下是不会生成dump文件的因为当前是WebRTC开源库内部检测到malloc返回NULL,直接调用abort,程序并没有发生C++上的异常,所以异常捕获是感知不到的(发生C++上的异常,异常捕获模块才能感知到),虽然给人一种程序发生崩溃闪退的感觉,但不会生成dump文件的。

2)内存不足,new失败抛出异常

       当程序内存不足时,使用new去动态申请内存失败,默认情况下会抛出bad_alloc异常。这种场景是产生了异常,如果程序中安装了异常捕获模块,且异常捕获模块感知到了,是会生成dump文件的。比如我们有次遇到的因为内存泄漏导致内存不足,导致new抛出了异常:(下图中抛出bad_alloc异常的代码就是new那句代码)

抛出的是std::bad_alloc异常,就是我们讲的bad_alloc异常,导致程序发生崩溃闪退(代码中没有对这个bad_alloc异常进行处理)。如果dump文件中显示new时抛出了bad_alloc异常,可以怀疑程序中可能存在内存泄漏,然后使用工具观察程序运行过程中的内存变化,去确定是否真的是内存泄漏导致的!

       这里还有一个细节,new失败抛出bad_alloc异常,不一定是内存不足导致的。也可能是程序中有堆内存越界,导致堆内存被破坏引发的堆内存被破坏,会导致程序导出胡乱的崩溃,一会再new的地方,一会在delete的地方。

        对于new失败时抛出bad_alloc异常导致程序崩溃,我们可以让new不要抛出异常,而是像malloc一样在申请内存失败时返回空指针,使用std::nothrow指定new不要抛出异常,如下:

#include <iostream>int main(){char *p = NULL;int i = 0;do{p = new(std::nothrow) char[10*1024*1024]; // 每次申请10MBi++;Sleep(5);}while(p);if(NULL == p){std::cout << "分配了 " << (i-1)*10 << " M内存"         //分配了 1890 Mn内存第 1891 次内存分配失败           << "第 " << i << " 次内存分配失败";}return 0;
}

此外,还可以使用try...catch来捕获异常:(因为异常被捕获处理了,程序也就不会再崩溃了)

#include <iostream>
using namespace std;int main(){char *p;int i = 0;try{do{p = new char[10*1024*1024];i++;Sleep(5);}while(p);}catch(const std::exception& e){std::cout << e.what() << "\n"<< "分配了" << i*10 << "M" << std::endl;}return 0;   
}

 虽然上面两种做法,都能让程序不再崩溃,但程序进程内存不足,业务已经没法正常展开和执行了,让程序还活着,意义也不大了。 

5.2、如何确定当前程序发生了内存泄漏?       

       如果根据问题现象或者初步分析,怀疑当前问题可能是内存泄漏导致的,我还需要观察程序运行时或者执行某个指定的操作时占用的内存是否在持续的增加

       可以在Windows任务管理器中找到程序进程,然后到程序中执行引发内存泄漏的操作,再到任务管理中查看进程占用的内存变化:

​但任务管理器中只能看到专用工作集、共享工作集、提交大小等内存信息,但这些内存不是程序进程占用的总的虚拟内存,一般我们需要查看程序占用的总的虚拟内存,最为直接,也最为直观!

       要查看程序占用的总虚拟内存,可以使用Process Explorer工具。Process Explorer在默认情况下是不显示虚拟内存列的,在进程列表的标题栏中右键点击,弹出右键菜单,点击“Select Columns”,在弹出的窗口中切换到“Process Memory”标签页下,将“Virtual Size”勾选上

​​进程列表中就会显示虚拟内存占用了:

​我们一般讲的内存不足,指的是虚拟内存的不足,所以需要查看程序进程的虚拟内存,使用Process Explorer查看程序进程的虚拟内存的变化,就可以确定是否在持续增长了。

       此外,也可以查看执行某个操作时内存泄漏了多少,具体的办法是,记录一下操作前的程序的虚拟内存,然后操作后再记录一下虚拟内存,两次虚拟内存之差,就是此次操作泄漏的内存大小!

5.3、使用工具排查内存泄漏    

       内存泄漏问题排查起来也比较复杂,有时需要尝试多种方法和工具,特别是代码量大的大型软件项目,排查的难度会更大。

​       一般我们要使用一些调试器或专用的内存分析工具来排查。在Windows中,可以使用Windbg(调试器)、Visual Leak Detector(VLD)、VMMap和DebugDiag等。在Linux系统中,则可以使用内存专用检测工具Valgrind和AddressSanitizer。

       有时内存泄漏点会定位不准,代码中包含了上百个业务模块,代码中到处都是动态new内存的,专业的内存检测工具可能也无法界定是哪块代码有泄漏泄露。所以可能需要尝试使用多个内存检测工具,但即便使用多个工具后可能也无法定位内存泄露点。

       关于VLD检测内存泄漏的完整案例,可以查看我的文章:

使用Visual Leak Detector(VLD)排查C++程序内存泄漏icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/135472681      关于Windbg检测内存泄漏的案例,可以查看我的文章:
使用Windbg排查C++程序内存泄漏问题icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/121295720使用 Windbg 的 !heap 命令分析内存泄漏icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/131576063      关于Linux系统上的Valgrind和AddressSanitizer工具,可以查看我的文章:
Windows和Linux下排查C++软件异常的常用调试器与内存检测工具详细介绍icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/126381865为什么选择C/C++内存检测工具AddressSanitizer?如何使用AddressSanitizer?icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/article/details/132863447       AddressSanitizer工具原先只支持Linux,现在也可以在Windows上使用了。微软在Visual Studio 2019的16.9版本们引入了AddressSanitizer,在安装Visual Studio 2019的16.9版本及以后的版本时,会默认安装AddressSanitizer工具:(默认勾选“C++ AddressSanitizer”)

​       对于如何在VS中如何使用AddressSanitizer内存分析工具,可以看一下微软官方文章的详细说明:

在Visual Studio中使用AddressSanitizericon-default.png?t=O83Ahttps://docs.microsoft.com/zh-cn/cpp/sanitizers/asan?view=msvc-170此处我就不详细展开了,大家需要使用的话,可以去详细研究一下。

       有时需要尝试使用多个内存检测工具去排查,之前记录了内存泄漏排查的专题,感兴趣也可以去看看:

C++内存泄漏排查专题icon-default.png?t=O83Ahttps://blog.csdn.net/chenlycly/category_12370029.html

6、最后

       本文系统总结了引发C++软件内存泄漏的常见原因和场景,给出了排查内存泄漏的若干工具和方法,并给出使用这些工具分析内存泄漏的实战问题实例,希望能给大家提供一定的参考。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

仓颉编程语言:编程世界的 “文化瑰宝”

我的个人主页 在当今编程领域百花齐放的时代&#xff0c;各种编程语言争奇斗艳&#xff0c;服务于不同的应用场景和开发者群体。然而&#xff0c;有这样一种编程语言&#xff0c;它承载着独特的文化内涵&#xff0c;宛如编程世界里一颗熠熠生辉的“文化瑰宝”&#xff0c;那就…

【Java】Spring Bean生命周期讲解

Spring bean生命周期的重要性 了解bean生命周期有助于更好掌握Spring框架&#xff0c;理解其对bean实例的管理和创建方式。有助于解决bean相关问题&#xff0c;如循环依赖问题&#xff0c;利于编写健壮、灵活、易维护的应用程序。 bean definition概念 spring容器实例化时&…

物联网工厂可视化监控平台:为智能制造打造的可视化大屏

01行业背景 随着技术的不断进步&#xff0c;物联网&#xff08;IoT&#xff09;已经成为推动数字化转型的核心力量。物联网通过连接各种设备和传感器&#xff0c;实现数据的实时收集、传输和分析&#xff0c;为各行各业带来了革命性的变化。随着5G、云计算、大数据等技术的成熟…

跟着问题学18——大模型基础transformer模型详解(4)解码器

3 Decoder层 图中可以看到&#xff0c;解码器Decoder其实和编码器Encoder大同小异&#xff0c;核心区别是在最下面额外多了一个掩码多头注意力层masked mutil-head attetion。在解码器中&#xff0c;自注意力层仅被允许“注意”输出序列中前面的单词信息。这是通过在自注意力计…

day-102 二叉树中的链表

思路 DFS,先将链表转换为字符串s在进行匹配 解题过程 对二叉树进行遍历&#xff0c;每到一个新节点判断当前的字符串t长度是否大于等于的长度&#xff0c;如果满足&#xff0c;再将t从末尾截取s.length()长度的子串与s进行匹配&#xff0c;若匹配成功&#xff0c;结果为true&a…

RACI矩阵在项目管理中的应用:优化任务管理

在团队合作中&#xff0c;最怕的就是责任不清、任务分工混乱。谁该做什么&#xff0c;谁对结果负责&#xff0c;谁需要提供帮助&#xff0c;谁需要被通知&#xff1f;如果这些问题没有理清楚&#xff0c;就很容易出现任务没完成、团队内耗或者“甩锅”的情况。RACI责任矩阵正是…

uniapp - 小程序实现摄像头拍照 + 水印绘制 + 反转摄像头 + 拍之前显示时间+地点 + 图片上传到阿里云服务器

前言 uniapp&#xff0c;碰到新需求&#xff0c;反转摄像头&#xff0c;需要在打卡的时候对上传图片加上水印&#xff0c;拍照前就显示当前时间日期地点&#xff0c;拍摄后在呈现刚才拍摄的图加上水印&#xff0c;最好还需要将图片上传到阿里云。 声明 水印部分代码是借鉴的…

Fetch处理大模型流式数据请求与解析

为什么有的大模型可以一次返回多个 data&#xff1f; Server-Sent Events (SSE)&#xff1a;允许服务器连续发送多个 data: 行&#xff0c;每个代表一个独立的数据块。 流式响应&#xff1a;大模型服务通常以流式响应方式返回数据&#xff0c;提高响应速度。 批量处理&#x…

怎么在电脑桌面上设置备忘录,桌面工作提醒小工具哪个好?

在现代的工作和生活中&#xff0c;我们经常需要记录重要的事项和提醒。而在电脑上设置备忘录&#xff0c;无疑是最方便和有效的方法之一。那么&#xff0c;怎么在电脑桌面上设置备忘录&#xff1f;又有哪个工作提醒小工具值得推荐呢&#xff1f; 以Windows系统为例&#xff0c…

EasyExcel简介和读写操作

EasyExcel简介 官网地址&#xff1a;EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 EasyExcel 的主要特点如下&#xff1a; 1、高性能&#xff1a;EasyExcel 采用了异步导入导出的方式&#xff0c;并且底层使用 NIO 技术实现&#xff0c;使得其在导入导出大…

【网络协议】路由信息协议 (RIP)

未经许可&#xff0c;不得转载。 路由信息协议&#xff08;Routing Information Protocol&#xff0c;简称 RIP&#xff09;是一种使用跳数&#xff08;hop count&#xff09;作为路由度量标准的路由协议&#xff0c;用于确定源网络和目标网络之间的最佳路径。 文章目录 什么是…

MySQL5.7.26-Linux-安装(2024.12)

文章目录 1.下载压缩包1.访问MySQL版本归档2.找到5.7.26并下载3.百度网盘 2.Linux安装1.卸载原来的MySQL8.0.26&#xff08;如果没有则无需在意&#xff09;1.查看所有mysql的包2.批量卸载3.删除残留文件**配置文件**&#xff08;默认路径&#xff09;&#xff1a; 4.**验证卸载…

《云原生安全攻防》-- K8s安全配置:CIS安全基准与kube-bench工具

在本节课程中&#xff0c;我们来了解一下K8s集群的安全配置&#xff0c;通过对CIS安全基准和kube-bench工具的介绍&#xff0c;可以快速发现K8s集群中不符合最佳实践的配置项&#xff0c;及时进行修复&#xff0c;从而来提高集群的安全性。 在这个课程中&#xff0c;我们将学习…

Flink源码解析之:如何根据算法生成StreamGraph过程

Flink源码解析之&#xff1a;如何根据算法生成StreamGraph过程 在我们日常编写Flink应用的时候&#xff0c;会首先创建一个StreamExecutionEnvironment.getExecutionEnvironment()对象&#xff0c;在添加一些自定义处理算子后&#xff0c;会调用env.execute来执行定义好的Flin…

RoboMIND:多体现基准 机器人操纵的智能规范数据

我们介绍了 RoboMIND&#xff0c;这是机器人操纵的多体现智能规范数据的基准&#xff0c;包括 4 个实施例、279 个不同任务和 61 个不同对象类别的 55k 真实世界演示轨迹。 工业机器人企业 埃斯顿自动化 | 埃夫特机器人 | 节卡机器人 | 珞石机器人 | 法奥机器人 | 非夕科技 | C…

sentinel集成nacos启动报[check-update] get changed dataId error, code: 403错误排查及解决

整合nacos报403错误 因为平台写的一个限流代码逻辑有问题&#xff0c;所以准备使用sentinel来限流。平台依赖里面已经引入了&#xff0c;之前也测试过&#xff0c;把sentinel关于nacos的配置加上后&#xff0c;启动一直输出403错误 [fixed-10.0.20.188_8848-test] [check-upda…

【Redis】 数据淘汰策略

面试官询问缓存过多而内存有限时内存被占满的处理办法&#xff0c;引出 Redis 数据淘汰策略。 数据淘汰策略与数据过期策略不同&#xff0c; 过期策略针对设置过期时间的 key 删除&#xff0c; 淘汰策略是在内存不够时按规则删除内存数据。 八种数据淘汰策略介绍 no evision&…

【畅购商城】详情页模块之评论

目录 接口 分析 后端实现&#xff1a;JavaBean 后端实现 前端实现 接口 GET http://localhost:10010/web-service/comments/spu/2?current1&size2 { "code": 20000, "message": "查询成功", "data": { "impressions&q…

Kafka高性能设计

高性能设计概述 Kafka高性能是多方面协同的结果&#xff0c;包括集群架构、分布式存储、ISR数据同步及高效利用磁盘和操作系统特性等。主要体现在消息分区、顺序读写、页缓存、零拷贝、消息压缩和分批发送六个方面。 消息分区 存储不受单台服务器限制&#xff0c;能处理更多数据…

HTML——13.超链接

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>超链接</title></head><body><!--超链接:从一个网页链接到另一个网页--><!--语法&#xff1a;<a href"淘宝网链接的地址"> 淘宝…