打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Wince和Windows Mobile下的内存监控

由于发现开发的程序有内存泄漏(Memory Leaks),所以编写了一个内存监控程序定位那个程序有Leaks。

所谓Memory Leaks就是程序在运行的过程中没有释放已经不再使用的内存,Memory Leaks不会直接导致程序core dump或者系统崩溃。只有程序在运行过程中不断的请求分配内存,而得到这部分的内存一直没有释放,直到系统再没有空闲的内存可以分配了,系统会变慢(有可能进行页交换所以变慢)甚至崩溃。

Wince(Windows Mobile同样适用,因为使用Wince的Kernel)的内存管理称谓虚拟内存结构(Virtual Memory Architecture)。在PC的世界,virtual memory是和物理内存(RAM)相对的概念,系统可以使用硬盘等设备作为内存使用,解决内存小的问题。但是在Wince里面,Virtual Memory的概念和PC不一样,他不是指硬盘或者其他物理设备,他是对物理内存的一个映射,在Wince里面所有应用程序都不可以直接操作物理内存的地址,只有通过Virtual Memory来分配和管理内存。每个进程内存管理单元(memory management unit )负责这些内存从虚拟地址(virtual addresses)到物理地址(physical addresses)的映射。

 

上图就是一个内存映射到例子,为什么Wince要通过内存管理单元来映射物理内存和虚拟内存,而不直接让应用程序访问物理内存呢?Wince是一个32位的系统,理论上可以管理4G的内存,可惜由于当前硬件设备的局限性,Wince运行的硬件环境一般只有很小的内存(64M,128M),所以Wince系统需要想方法节省物理内存的使用。在虚拟内存结构下,Wince依然提供4G虚拟内存给用户,其中2G用于kernel模式,2G用于用户进程。当程序需要载入数据到内存时,Wince会检查该数据是否已经在物理内存里面,如果在,就建立多一个映射关联到虚拟内存,如上图中的Device Buffer被使用了2次,所以有两个虚拟内存地址,确映射到同一个物理内存地址上 。这样做的目的是Process理论上可以使用4G的内存,具体的映射由Wince来负责,Wince也可以因此提高物理内存的使用率。

由于虚拟内存结构,因此我们可以监控每个进程的虚拟内存使用情况,不能监控到具体的每个进程的物理内存使用率。虚拟内存主要分三类:

Free: 没有分配或者使用的虚拟内存。

Reserved:被预订,但是还没有映射到物理内存的虚拟内存。

Committed:先被预订,同时已经映射到物理内存的虚拟内存,也就是正在在使用中的内存。

以下是内存监控的代码。

class MemoryMonitor
{
private:
    
static const int TH32CS_SNAPNOHEAPS = 0x40000000;

public:
    
void ShowTotalMemoryInfo()
    {
        MEMORYSTATUS status;
        GlobalMemoryStatus(
&status);
        wprintf(L
"\n%s Memory Load:%ld, TotalPhys:%ld, AvailPhys:%ld, TotalVirtual:%ld, AvailVirtual:%ld \n"
            getTimeStamp(),
            status.dwMemoryLoad, 
            status.dwTotalPhys, 
            status.dwAvailPhys, 
            status.dwTotalVirtual, 
            status.dwAvailVirtual);
    }

public:
    
void ShowProcessesMemoryInfo()
    {
        HANDLE snapShot 
= INVALID_HANDLE_VALUE;
        
try
        {                                                             
            snapShot 
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);
            
if (snapShot != INVALID_HANDLE_VALUE)
            {
                PROCESSENTRY32 processEntry;
                processEntry.dwSize 
= sizeof(PROCESSENTRY32);
                BOOL ret 
= Process32First(snapShot, &processEntry);
                
while (ret == TRUE)
                {
                    wprintf(L
"%s %s, %X, MemoryBase:%X, ThreadCnt:%d, ",
                        getTimeStamp(),
                        processEntry.szExeFile, 
                        processEntry.th32ProcessID, 
                        processEntry.th32MemoryBase,
                        processEntry.cntThreads); 
                    
                    ShowMemoryInfo(processEntry.th32MemoryBase);
                    ShowHeapInfo(processEntry.th32ProcessID);
                    ret 
= Process32Next(snapShot, &processEntry);
                }
                
if (snapShot != INVALID_HANDLE_VALUE)
                {
                    CloseToolhelp32Snapshot(snapShot);
                }
            }
            
else
            {
                DWORD err 
= GetLastError();
                LPVOID lpMsgBuf;
                FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                NULL,
                                err,
                                
0// Default language
                                (LPTSTR) &lpMsgBuf,
                                
0,
                                NULL);
                wprintf(L
"ERROR: %s", lpMsgBuf);
            }
    
//  } __except ( Filter(GetExceptionCode(), GetExceptionInformation()) )
        }
        
catch (
)
        {
            
if (snapShot != INVALID_HANDLE_VALUE)
            {
                CloseToolhelp32Snapshot(snapShot);
            }
        }
    }

private:
    
void ShowMemoryInfo(DWORD baseAddress)
    {
        DWORD startAddress 
= baseAddress;
        DWORD endAddress 
= startAddress + 0x40000000;
        DWORD address 
= startAddress;

        DWORD committedMemory 
= 0;
        DWORD reservedMemory 
= 0;
        DWORD freeMemory 
= 0;
        
while (address < endAddress)
        {
            MEMORY_BASIC_INFORMATION memoryInfo;
            SIZE_T rv 
= ::VirtualQuery((LPVOID)address, &memoryInfo, sizeof(memoryInfo));
            
if (rv != 0)
            {
                
if (memoryInfo.State == MEM_COMMIT)
                {
                    committedMemory 
+= memoryInfo.RegionSize;
                }
                
if (memoryInfo.State == MEM_RESERVE)
                {
                    reservedMemory 
+= memoryInfo.RegionSize;
                }
                
if (memoryInfo.State == MEM_FREE)
                {
                    freeMemory 
+= memoryInfo.RegionSize;
                }
                address 
+= memoryInfo.RegionSize;
            }
            
else
            {
                
break;
            }
            
        }
        wprintf(L
"Cmtd:%ld, Rsvd:%ld, Free:%ld, ", committedMemory, reservedMemory, freeMemory);
    }

    
void ShowHeapInfo(DWORD processId)
    {
        HANDLE snapShot 
= INVALID_HANDLE_VALUE;
        DWORD heapSize 
= 0;

        
// Need to use __try __except on ToolHelp API.
        
// If a process is being destroyed (shutdown), the API crashes (AV on NULL pointer)
        
// Can use try catch if /EHa compiler settings is used
        
//  __try
        try
        {
            snapShot 
= CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, processId);
            
            
if (snapShot != INVALID_HANDLE_VALUE)
            {
                HEAPLIST32 heapList;
                HEAPENTRY32 heapEntry;

                heapList.dwSize 
= sizeof(HEAPLIST32);
                heapEntry.dwSize 
= sizeof(HEAPENTRY32);
                BOOL ret 
= Heap32ListFirst(snapShot, &heapList);
                
while (ret == TRUE)
                {
                    BOOL ret2 
= Heap32First(snapShot, &heapEntry, heapList.th32ProcessID, heapList.th32HeapID);

                    
// Loop the blocks in the heaps to get the size
                    while (ret2 == TRUE)
                    {
                        
if (heapEntry.dwFlags != LF32_FREE)
                        {
                            heapSize 
+= heapEntry.dwBlockSize;
                        }
                        ret2 
= Heap32Next(snapShot, &heapEntry);
                    }

                    ret 
= Heap32ListNext(snapShot, &heapList);
                }
                wprintf(L
"Heap:%ld\n", heapSize);
                
if (snapShot != INVALID_HANDLE_VALUE)
                {
                    CloseToolhelp32Snapshot(snapShot);
                }
            }
            
else
            {
                wprintf(L
"Heap:ERROR\n");
            }
    
//  } __except ( Filter(GetExceptionCode(), GetExceptionInformation()) )
        } catch (
)
        {
            heapSize 
= 0;
            
if (snapShot != INVALID_HANDLE_VALUE)
            {
                CloseToolhelp32Snapshot(snapShot);
            }
        }
    }
};
getTimeStamp()
WCHAR * getTimeStamp()
{
    SYSTEMTIME currentTime;
    std::
string timeString = "";
    
char tempBuffer[32];
    
    
//get the current time - use win API as CE does not support c time functions
    GetLocalTime(&currentTime);

    
//Add hour
    if(currentTime.wHour < 10)
        timeString 
+= "0";
    timeString 
+= _itoa(currentTime.wHour,tempBuffer,10);
    
    
//add delimiter
    timeString += ":";

    
//add minute
    if(currentTime.wMinute < 10)
        timeString 
+= "0";
    timeString 
+= _itoa(currentTime.wMinute,tempBuffer,10);

    
//add delimiter
    timeString += ":";        
    
    
//add second
    if(currentTime.wSecond < 10)
        timeString 
+= "0";
    timeString 
+= _itoa(currentTime.wSecond,tempBuffer,10);
    
    
//add delimiter
    timeString += " ";

    
//add day
    if(currentTime.wDay < 10)
        timeString 
+= "0";
    timeString 
+= _itoa(currentTime.wDay,tempBuffer,10);

    
//add delimiter
    timeString += "/";
    
    
//add month
    if(currentTime.wMonth < 10)
        timeString 
+= "0";
    timeString 
+= _itoa(currentTime.wMonth,tempBuffer,10);
    
    
//add delimiter
    timeString += "/";
    
    
//add year
    timeString += _itoa(currentTime.wYear,tempBuffer,10);
    
    
//Copies it to a (char *) which is then passed to any function thatx wants TimeStamp
    static WCHAR timeStamp[64];
    swprintf(timeStamp, L
"%S", timeString.c_str());

    
return timeStamp;
}

 

GlobalMemoryStatus取出整个Wince系统的内存使用信息,包括负载,物理内存和虚拟内存信息。

CreateToolhelp32Snapshot能取出进程信息,进程的heap信息。

VirtualQuery能取出进程的虚拟内存信息。

在监控内存使用情况时,监控进程的heap信息十分重要,wincestack是固定大少的,如果heap不断的增长,说明进程在不断的分配动态内存。

int _tmain(int argc, _TCHAR* argv[])
{
    MemoryMonitor memoryMonitor;
    
while(1)
    {
        memoryMonitor.ShowTotalMemoryInfo();
        memoryMonitor.ShowProcessesMemoryInfo();
        ::Sleep(
60000);
    }
    
    
char str[127];
    scanf(str);
    
return 0;
}

上面的代码演示每分钟打印一次内存信息。

int _tmain(int argc, _TCHAR* argv[])
{
    
char* p;

    
while(1)
    {
        p 
= new char[1024];
        Sleep(
1000);
    }
    
return 0;
}

上面是一个内存泄漏的程序,每秒钟泄漏1k的内存。可以用这个程序检验内存监控程序的有效性。


参考文档

Stanislav Pavlov, Pavel Belevsky :  Windows? Embedded CE 6.0 Fundamentals

GlobalMemoryStatus

MEMORYSTATUS

CreateToolhelp32Snapshot

CloseToolhelp32Snapshot

Process32First

Task Manager for Windows Mobile and Windows CE

What is Virtual Memory?

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
wince中的定时关机的实现
Windows核心编程(第五版)笔记 第七章 线程调度、优先级 (Thread Sched...
Windows CE 内存管理
WinCEOAL中的RAM定制函数-ARM
WINCE下进程间通信(一) .
用HOOK Call提升挂的效率和及时性
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服