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和Windows Mobile下的内存监控 

上图就是一个内存映射到例子,为什么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  (Wince和Windows Mobile下的内存监控)
        {
            
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  (Wince和Windows Mobile下的内存监控)
        {
            heapSize 
=   0 ;
            
if  (snapShot  !=  INVALID_HANDLE_VALUE)
            {
                CloseToolhelp32Snapshot(snapShot);
            }
        }
    }
};
Wince和Windows Mobile下的内存监控 getTimeStamp()

 

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?

你可能感兴趣的:(Windows Mobile)