代码改变世界

利用压力测试来保证软件的质量(四) 内存泄露问题

  王克伟  阅读(1115)  评论(0编辑  收藏  举报

内存泄露

这是实际产品中的一个严重的内存泄漏的Bug,这样描述的:

[MemoryLeak] Memory comsuption keeps inscreasing when open/close applications. We saw similar issue on several different applications. Here's some data for WMP and Calculator:

WMP:
Iteration                    0    1    2    3    4    5    6    7    8    9    10
Free Mem (MB)    133.83    133.51    133.33    133.17    132.94    132.80    132.64    132.48    132.32    132.16    132.00

Calculator:
Iteration                     0    1    2    3    4    5    6    7    8    9    10
Free Mem (MB)    133.88    133.13    132.92    132.80    132.66    132.55    132.46    132.33    132.21    132.10    131.98

泄露发生在private/shellw/gserver/shell32g/san_cmn.cpp L275:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static BOOL FreeAppData( SANMSG *psm )
{
    BOOL dwRes = FALSE;
    DWORD dwExitCode;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, psm->dwProcessID);
 
    if( psm->pvAppData &&
        (hProcess && GetExitCodeProcess( hProcess, &dwExitCode)) &&
        STILL_ACTIVE == dwExitCode )
    {
        switch (psm->dwNotify)
        {
        case SAN_NOTIFY_LAUNCH:
            __try {
 
                dwRes = !RemoteHeap::LocalFree(psm->pvAppData, psm->dwProcessID);
                psm->pvAppData = NULL;
            } __except(WatsonReportFault(GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER)) {
                DEBUGMSG(ZONE_SAN,(TEXT("freeappdata threw exception.\r\n")));
                dwRes = FALSE;
            }
            break;
        }
        //CloseHandle(hProcess);
    }
    if (hProcess)
        CloseHandle(hProcess); //应该在此位置关闭进程的句柄!一个疏忽造成了很严重的内存泄露。
 
    return dwRes;
}

我查一下,这个问题在移植过程中疏忽造成的(不断的代码修改造成的,看来纵有百虑总有一疏啊)。

FreeAppData API在程序启动时会调用,而在关闭程序时不会调用,比如启动WMP时:

1
2
3
4
5
6
7
8
9
10
11
12
13
SHELL32!FreeAppData(SANMSG * 0x007ab720)  line 253
SHELL32!XSHAppNotifyDone(HWND__ * 0x700578a0, long 0x00000000, unsigned long 0x00000001, void * 0x009dfc30)  line 176 + 12 bytes
AYGSHELL!xxx_SHAppNotifyDone(HWND__ *, long, unsigned long, void *)  line 40 + 23 bytes
WMPOCX!CPlayerWindow::OnSANMessage(HWND__ *, unsigned int, long, bool &)  line 1104 + 20 bytes
WMPOCX!CPlayerWindow::WndProc(HWND__ *, unsigned int, unsigned int, long)  line 143 + 44 bytes
GWES!WindowProcCallback(void * 0x07f60102, long (HWND__ *, unsigned int, unsigned int, long)* 0x423637c0, CWindow * 0x700578a0, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, bool * 0xd890fb53)  line 3198 + 21 bytes
GWES!CWindow::CallWindowProcW_I(CePtr_t<long (__cdecl*)(HWND__ *,unsigned int,unsigned int,long)> {...}, HWND__ * 0x700578a0, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, SendMsgEntry_t * 0x00000000)  line 3403 + 33 bytes
GWES!MsgQueue::DispatchMessageW_I(const tagMSG * 0x0003fb50)  line 4967 + 38 bytes
GWES!PixelDoubled_t::DispatchMessageW_I(const tagMSG * 0x0003fb50)  line 2083 + 15 bytes
COREDLL!DispatchMessageW(const tagMSG *)  line 2947 + 9 bytes
WMPOCX!StartThePlayer(HINSTANCE__ *, int)  line 92 + 9 bytes
WMPLAYER!PlayerEntry()  line 99 + 9 bytes
COREDLL!MainThreadBaseFunc(void *, const wchar_t *, const wchar_t *, HINSTANCE__ *, HINSTANCE__ *, HINSTANCE__ *)  line 1072 + 60 bytes

 

奇怪的是启动IE时不会调用到这个API,而启动闹铃时会:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SHELL32!FreeAppData(SANMSG * 0x009b2940)  line 253
SHELL32!XSHAppNotifyDone(HWND__ * 0x70053000, long 0x00000000, unsigned long 0x00000001, void * 0x009dfc30)  line 176 + 12 bytes
AYGSHELL!xxx_SHAppNotifyDone(HWND__ *, long, unsigned long, void *)  line 40 + 23 bytes
CLOCKDLL!HandleSAN_NOTIFY(tagAppState *, HWND__ *, unsigned int, long)  line 1095 + 20 bytes
CLOCKDLL!WndProc(HWND__ *, unsigned int, unsigned int, long)  line 1390 + 21 bytes
GWES!WindowProcCallback(void * 0x011a002a, long (HWND__ *, unsigned int, unsigned int, long)* 0x417a34a0, CWindow * 0x70053000, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, bool * 0xd76ffb53)  line 3198 + 21 bytes
GWES!CWindow::CallWindowProcW_I(CePtr_t<long (__cdecl*)(HWND__ *,unsigned int,unsigned int,long)> {...}, HWND__ * 0x70053000, unsigned int 0x0000c003, unsigned int 0x00000003, long 0x00000000, SendMsgEntry_t * 0x00000000)  line 3403 + 33 bytes
GWES!MsgQueue::DispatchMessageW_I(const tagMSG * 0x0002f9dc)  line 4967 + 38 bytes
GWES!PixelDoubled_t::DispatchMessageW_I(const tagMSG * 0x0002f9dc)  line 2083 + 15 bytes
COREDLL!DispatchMessageW(const tagMSG *)  line 2947 + 9 bytes
COMMCTRL!_RealPropertySheet(PROPDATA *)  line 3821 + 9 bytes
COMMCTRL!PropertySheetW(const _PROPSHEETHEADERW *)  line 3992 + 9 bytes
CLOCKDLL!DisplayPropSheet(HWND__ *, tagAppState *)  line 187 + 13 bytes
CLOCKDLL!StartClock(HINSTANCE__ *, unsigned short *, int, HINSTANCE__ *)  line 754 + 16 bytes
CLOCK!WinMain(HINSTANCE__ *, HINSTANCE__ *, unsigned short *, int)  line 27 + 21 bytes
COREDLL!MainThreadBaseFunc(void *, const wchar_t *, const wchar_t *, HINSTANCE__ *, HINSTANCE__ *, HINSTANCE__ *)  line 1072 + 60 bytes


当然健全的测试机制是发现这些内存泄漏的问题的前提。

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示