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);
            }
        }
    }
};
复制代码
ContractedBlock.gif 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?


    本文转自Jake Lin博客园博客,原文链接:http://www.cnblogs.com/procoder/archive/2009/04/01/1427187.html,如需转载请自行联系原作者



相关文章
|
3月前
|
Arthas 监控 Java
Arthas 可以用于监控和诊断在 Windows 系统下部署的 Tomcat 服务
Arthas 可以用于监控和诊断在 Windows 系统下部署的 Tomcat 服务
181 2
|
3月前
|
存储 缓存 Java
释放C盘空间:释放Windows休眠文件和关闭虚拟内存
在 Windows 11 专业版中,可以通过以下步骤来释放休眠文件(Hibernate File),以释放磁盘空间。休眠文件是系统休眠(Hibernate)功能所需要的文件,它保存了系统的当前状态,以便在休眠状态下恢复。如果你不使用休眠功能,如果因为C盘空间不足,可以考虑释放这个文件来腾出磁盘空间。
3708 0
|
3月前
|
C++ Windows
windows下内存检测工具
windows下内存检测工具
|
24天前
|
存储 监控 异构计算
【Python】GPU内存监控脚本
【Python】GPU内存监控脚本
|
1月前
|
缓存 监控 Linux
linux 内存监控
linux 内存监控
16 1
|
2月前
|
监控 Linux
|
4月前
|
监控 算法 搜索推荐
C++内部监控软件:内存管理与性能调优的完美结合
在当今高度竞争的软件开发领域,内存管理和性能调优是构建高效应用的两个关键方面。本文将介绍一种基于C++的内部监控软件,通过结合精细的内存管理和有效的性能调优,实现了出色的应用性能。我们将深入探讨一些示例代码,演示如何在代码层面实现内存管理和性能优化,最后介绍如何将监控到的数据自动提交到网站。
225 1
|
4月前
|
缓存 C# Windows
一款.NET开源的小巧、智能、免费的Windows内存清理工具 - WinMemoryCleaner
一款.NET开源的小巧、智能、免费的Windows内存清理工具 - WinMemoryCleaner
|
4月前
|
监控 安全 API
7.2 Windows驱动开发:内核注册并监控对象回调
在笔者上一篇文章`《内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。
30 0
7.2 Windows驱动开发:内核注册并监控对象回调
|
4月前
|
监控 安全 API
7.6 Windows驱动开发:内核监控FileObject文件回调
本篇文章与上一篇文章`《内核注册并监控对象回调》`所使用的方式是一样的都是使用`ObRegisterCallbacks`注册回调事件,只不过上一篇博文中`LyShark`将回调结构体`OB_OPERATION_REGISTRATION`中的`ObjectType`填充为了`PsProcessType`和`PsThreadType`格式从而实现监控进程与线程,本章我们需要将该结构填充为`IoFileObjectType`以此来实现对文件的监控,文件过滤驱动不仅仅可以用来监控文件的打开,还可以用它实现对文件的保护,一旦驱动加载则文件是不可被删除和改动的。
29 1
7.6 Windows驱动开发:内核监控FileObject文件回调