内存检测工具50 (内存检测工具5.0怎么看)

本来是打算使用腾讯的tMemMonitor内存检测工具排查我们C++软件的内存泄露问题,结果无意中发现了两例因为内存异常操作引发的软件崩溃,在这里记录并分享一下。

1、使用tMemMonitor工具遇到了问题

最近在根据新需求在开发新版本的软件,其中有一块是为了解决视频会议中的声音噪音大、有啸叫的问题,我们将音编解码方案换成开源的webrtc音频库,结果换了以后,程序运行会有很严重的内存泄漏,运行半个左右内存就能飙到1G多,这个问题比较严重,必须解决掉,于是使用尝试着使用腾讯的tMemMonitor内存泄漏检测工具排查一下,看看内存泄漏发生在哪个模块中。

使用tMemMonitor检测内存泄漏的步骤如下所示:

启动tMemMonitor后,将目标进程添加到列表中:

然后右键点击条目,在弹出的右键菜单中,点击“启动进程”,将进程运行起来。要捕捉内存泄漏信息,必须使用tMemMonitor启动,然后运行一段时间后,将目标程序退出,然后tMemMonitor就能生成内存泄漏检测报告了。

但是奇怪的是,我们的程序退出后,tMemMonitor并没有生成检测报告,难道是我们的程序没有内存泄漏,或者是tMemMonitor工具并没有声称的那么强大,有些情况下捕捉不到内存泄漏?

查了tMemMonitor官方网站上的相关说明,tMemMonitor明确要求需要正常退出目标程序才能生成检测报告。难道是我们程序并没有正常退出呢?想到我们以前做过的一个策略:我们的程序退出时会启动一个辅助监控进程,如果若干秒后进程还没有退出,就强制将进程结束掉(调用TerminateProcess强杀进程),如果退出的比较慢,可能是辅助进程将我们的主进程杀掉了,即我们的程序没有正常的退出,所以没捕捉到内存泄漏的信息。

于是将我们程序退出时启动辅助监控进程的代码注释掉,重新编译,重新添加到tMemMonitor中测试。结果又遇到了新的情况,退出时竟然发生了崩溃,这又是不正常的退出程序,tMemMonitor肯定还是捕获不到,所以必须先将这个崩溃解决掉。

2、同一块内存被delete两次导致崩溃

于是将windbg附加到重新启动的进程上,然后将进程关闭,复现刚才的崩溃,然后windbg捕捉到异常信息如下电脑所示:

事先已经将音视频编解码库的pdb符号库取过来了,路径已经添加到windbg中,所以直接在windbg中输入kn,在打印出来的函数调用堆栈能看到函数名和函数中具体的行号,于是找来音视频编解码组的同事前来对照代码一起分析,他按照windbg中显示的函数及函数中的行号,找到对应的代码块,如下所示:

电脑

看到这个代码,同事突然想起来,在音频webrtc模式下,音频采集指针m_pAudioCap和音频解码播放指针m_pAudioPlay指向的是同一个webrtc音频处理对象,所以上面的代码导致了同一个对象被delete释放了两次,即第二次delete时delete的是已经释放的内存,所以导致了崩溃。

解决办法是,在将webrtc音视频处理对象销毁时,同时将m_pAudioCap和m_pAudioPlay两个指针都置空:

程序在退出时,会销毁音视频编解码库中的音视频编解码对象,所以报出了这个问题。因为之前我们的程序在退出时,会启动一个辅助进程在退出超时时将进程杀掉的,所以这个问题之前一直没暴露出来。

3、对全局区内存执行了delete操作导致的崩溃

于是让音视频编解码组的同事把代码修改掉,将编译后的库发给我,我这边还要继续排查内存泄漏的问题。结果退出时还是有崩溃,于是又用windbg挂载上去,排查到另一个内存异常操作引起的崩溃,代码如下:

这是在delete m_ptInst指针变量指向的对象时产生了崩溃,于是查看了一下指针变量m_ptInst在初始化时传入的是什么对象,发现是将一个静态类对象的地址传给了指针变量m_ptInst:

这个类对象因为是静态的,是在全局内存区分配内存的,不是在堆内存上分配的,所以是不能进行delete操作,delete只能释放堆内存。

C/C++软件中的内存一般栈内存、堆内存、全局内存区和常量内存区,要搞清楚它们之间的区别,不能混淆!

总结

这两个崩溃都解决了,程序就能正常退出了,tMemMonitor也就能捕获到内存泄漏的报告了。

没曾想,为了排查内存泄漏问题,竟然发现了两个内存异常操作引起的崩溃问题。

其实这两个崩溃问题并不复杂,只是开发人员在写代码时不够仔细,不够规范,导致了这些问题,这也告诉我们平时写代码时要尽量规范严谨一点!


电脑