一、发现内存泄漏
使用VS2022,发现提示有内存泄漏,检查了所有的new,确认都有相应的delete释放。
Detected memory leaks!
Dumping objects ->
{1914} normal block at 0x0000021FDFFBD2E0, 48 bytes long.
Data: <` > 60 E2 FB DF 1F 02 00 00 CD CD CD CD CD CD CD CD
{1907} normal block at 0x0000021FDFFC22C0, 16 bytes long.
Data: <8 > 38 F5 FA DF 1F 02 00 00 00 00 00 00 00 00 00 00
{1838} normal block at 0x0000021FDFFBCE10, 48 bytes long.
Data: < > 90 DF FB DF 1F 02 00 00 CD CD CD CD CD CD CD CD
{1831} normal block at 0x0000021FDFFCCE10, 16 bytes long.
Data: <8 > 38 FB FA DF 1F 02 00 00 00 00 00 00 00 00 00 00
{1699} normal block at 0x0000021FDFFBCB00, 48 bytes long.
Data: <@ > 40 BB F8 DF 1F 02 00 00 88 BB F8 DF 1F 02 00 00
{1692} normal block at 0x0000021FDFFCD450, 16 bytes long.
Data: <8 > 38 F9 FA DF 1F 02 00 00 00 00 00 00 00 00 00 00
{1683} normal block at 0x0000021FDFFBD0B0, 48 bytes long.
Data: < X > 10 AF F8 DF 1F 02 00 00 58 AF F8 DF 1F 02 00 00
{1676} normal block at 0x0000021FDFFCCDC0, 16 bytes long.
Data: < > B8 F4 FA DF 1F 02 00 00 00 00 00 00 00 00 00 00
Object dump complete.
手工很难排查,需要使用工具了。
二、安装VLD
从往上搜索下载VLD,Visual Leak Detector | Enhanced Memory Leak Detection for Visual C++
下载最新版,双击安装,默认路径在C:\Program Files (x86)\Visual Leak Detector\下。
三、设置编译环境
在主程序的stdafx.h文件中,afxwin.h头文件之前,加入vld.h头文件。
// 关闭 MFC 对某些常见但经常可放心忽略的警告消息的隐藏
#define _AFX_ALL_WARNINGS
#include "vld.h"
#include <afxwin.h> // MFC 核心组件和标准组件
#include <afxext.h> // MFC 扩展
#include <afxdisp.h> // MFC 自动化类
添加头文件路径:C:\Program Files (x86)\Visual Leak Detector\include;
添加vld.lib类库应用
把C:\Program Files (x86)\Visual Leak Detector\bin\Win64和C:\Program Files (x86)\Visual Leak Detector\lib\Win64目录下的所有文件都复制到当前工程的.\x64\Debug路径下。添加类库的路径:
然后编译即可。
四、检测内存泄漏
运行之后,查看程序加载的模块,确认vld类库加载成功
退出程序,显示VLD已经检测出内存泄漏,单给出的信息不明所以。。
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 22 at 0x00000000962B2B80: 248 bytes ----------
Leak Hash: 0x656456F1, Count: 1, Total 248 bytes
Call Stack (TID 19720):
ucrtbased.dll!malloc_dbg()
mfc140d.dll!0x00007FFFC44332DA()
mfc140d.dll!0x00007FFFC4579350()
mfc140d.dll!0x00007FFFC457938D()
mfc140d.dll!0x00007FFFC49A8C95()
mfc140d.dll!0x00007FFFC4908D3A()
mfc140d.dll!0x00007FFFC4908430()
mfc140d.dll!0x00007FFFC4A2C92C()
mfc140d.dll!0x00007FFFC4A26474()
mfc140d.dll!0x00007FFFC4531F05()
mfc140d.dll!0x00007FFFC451D9E0()
mfc140d.dll!0x00007FFFC451C1B8()
mfc140d.dll!0x00007FFFC444839B()
mfc140d.dll!0x00007FFFC47A163E()
E:\Work\09.SourceCode\ArtecRoth.cpp (71): ArtecRoth.exe!CArtecRothApp::InitInstance()
mfc140d.dll!0x00007FFFC4A2CA3C()
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\appmodul.cpp (26): ArtecRoth.exe!WinMain()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (107): ArtecRoth.exe!invoke_main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (288): ArtecRoth.exe!__scrt_common_main_seh() + 0x5 bytes
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (331): ArtecRoth.exe!__scrt_common_main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_winmain.cpp (17): ArtecRoth.exe!WinMainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x14 bytes
ntdll.dll!RtlUserThreadStart() + 0x21 bytes
Data:
00 00 00 00 00 00 00 00 08 80 30 96 28 01 00 00 ........ ..0.(...
28 1A 10 6F 00 00 00 00 58 75 D0 C4 FF 7F 00 00 (..o.... Xu......
28 0A 2E 96 28 01 00 00 FD 19 10 11 00 00 00 00 (...(... ........
C8 75 D0 C4 FF 7F 00 00 88 2B 2B 96 28 01 00 00 .u...... .++.(...
B3 14 10 C4 FF FF FF FF 98 75 D0 C4 FF 7F 00 00 ........ .u......
10 0A 2E 96 28 01 00 00 20 1A 10 3E 00 00 00 00 ....(... ...>....
A8 75 D0 C4 FF 7F 00 00 00 00 00 00 00 00 00 00 .u...... ........
91 18 10 01 00 00 00 00 68 75 D0 C4 FF 7F 00 00 ........ hu......
40 0A 2E 96 28 01 00 00 4B 1A 10 65 00 00 00 00 @...(... K..e....
88 75 D0 C4 FF 7F 00 00 E8 2B 2B 96 28 01 00 00 .u...... .++.(...
22 1A 10 3C 00 00 00 00 B8 75 D0 C4 FF 7F 00 00 "..<.... .u......
E0 09 2E 96 28 01 00 00 50 1A 30 1B 00 00 00 00 ....(... P.0.....
D8 75 D0 C4 FF 7F 00 00 70 0A 2E 96 28 01 00 00 .u...... p...(...
12 17 30 FD FF FF FF FF E8 75 D0 C4 FF 7F 00 00 ..0..... .u......
48 2C 2B 96 28 01 00 00 47 1A 30 53 00 00 00 00 H,+.(... G.0S....
F8 75 D0 C4 FF 7F 00 00 .u...... ........
说明内存泄露没有发生在主程序中,把上面的编译设置从主程序中去除,在下面调用的dll工程中按同样的方法设置,多个dll工程的话,需要挨个试。
在dll工程中设置,重新编译后,运行结果果然找到了具体位置。
---------- Block 3747 at 0x00000000C2B10AD0: 48 bytes ----------
Leak Hash: 0x60ABD716, Count: 1, Total 48 bytes
Call Stack (TID 20788):
mfc140d.dll!0x00007FFFC4093200()
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\xmemory (78): AtcTransRoth.dll!std::_Default_allocate_traits::_Allocate()
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\xmemory (235): AtcTransRoth.dll!std::_Allocate<16,std::_Default_allocate_traits,0>() + 0xC bytes
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\xmemory (836): AtcTransRoth.dll!std::allocator<TransCoil *>::allocate()
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\vector (1673): AtcTransRoth.dll!std::vector<TransCoil *,std::allocator<TransCoil *> >::_Reallocate_exactly() + 0x10 bytes
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\vector (1750): AtcTransRoth.dll!std::vector<TransCoil *,std::allocator<TransCoil *> >::reserve()
E:\Work\AtcArray.h (114): AtcTransRoth.dll!vector<TransCoil *>::Reserve() + 0x13 bytes
E:\Work\AtcRothData.cpp (182): AtcTransRoth.dll!CAtcRothData::GetCoils()
E:\Work\AtcRothData.cpp (831): AtcTransRoth.dll!CAtcRothData::CreateElement() + 0x1B bytes
E:\Work\AtcRothData.cpp (616): AtcTransRoth.dll!CAtcRothData::LoadSchemaFromXml() + 0x13 bytes
E:\Work\AtcRothData.cpp (34): AtcTransRoth.dll!CAtcRothData::LoadData() + 0x21 bytes
E:\Work\ArtecRothDlg.cpp (207): ArtecRoth.exe!CArtecRothDlg::OnEnChangeMfceditbrowse1() + 0x41 bytes
mfc140d.dll!0x00007FFFC44A21CC()
mfc140d.dll!0x00007FFFC44A1A06()
mfc140d.dll!0x00007FFFC4508F2C()
从提示的信息上看是vector申请的内存没有释放。通过仔细查看, 确认是TransWindingElementCoil 结构体中出现了内存泄漏,问题出在了结构体的继承上。
struct TransWindingElement
{
CString chName;
CString chLabel;
AemTransElement emType;
AemTransJoint emJoint;
};
struct TransWindingElementCoil : TransWindingElement
{
vector<TransCoil*> arCoil;
};
在delete释放TransWindingElementCoil 对象是,由于保存的是父结构体的指针,只调用了TransWindingElement的析构函数,而没有调用TransWindingElementCoil 对象的析构函数,造成了vector对象所在内存空间的泄漏。
五、处理方法
给父结构体加虚析构函数
struct TransWindingElement
{
virtual ~TransWindingElement() {}
CString chName;
CString chLabel;
AemTransElement emType;
AemTransJoint emJoint;
};
struct TransWindingElementCoil : TransWindingElement
{
vector<TransCoil*> arCoil;
};
运行再无内存泄漏。
六、结束
在编程是需要时刻观察内存泄漏信息,出现内存泄漏要及时处理。堆积时间越长,则越难处理,尤其在运行计算的大循环中,调式内存泄漏极其麻烦。