`
lxlsp
  • 浏览: 6908 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

全面介绍Windows内存管理机制及C++内存分配实例 之 内存状态查询

阅读更多

1. 内存状态查询函数
1.1系统信息
Windows 提供API可以查询系统内存的一些属性,有时候我们需要获取一些页面大小、分配粒度等属性,在分配内存时用的上。
请看以下C++程序:
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
cout<<"机器属性:"<<endl;
cout<<"页大小="<<sysInfo.dwPageSize<<endl;
cout<<"分配粒度="<<sysInfo.dwAllocationGranularity<<endl;
cout<<"用户区最小值="<<sysInfo.lpMinimumApplicationAddress<<endl;
cout<<"用户区最大值="
<<sysInfo.lpMaximumApplicationAddress<<endl<<endl;
结果如下:

 可以看出,页面大小是4K,区域分配粒度是64K,进程用户区是0x0001 0000~0x7FFE FFFF。

1.2内存状态
·内存状态可以获取总内存和可用内存,包括页文件和物理内存。
请看以下C++程序:
MEMORYSTATUS memStatus;
GlobalMemoryStatus(&memStatus);
cout<<"内存初始状态:"<<endl;
cout<<"内存繁忙程度="<<memStatus.dwMemoryLoad<<endl;
cout<<"总物理内存="<<memStatus.dwTotalPhys<<endl;
cout<<"可用物理内存="<<memStatus.dwAvailPhys<<endl;
cout<<"总页文件="<<memStatus.dwTotalPageFile<<endl;
cout<<"可用页文件="<<memStatus.dwAvailPageFile<<endl;
cout<<"总进程空间="<<memStatus.dwTotalVirtual<<endl;
cout<<"可用进程空间="<<memStatus.dwAvailVirtual<<endl<<endl;
结果如下:

 可以看出,总物理内存是1G,可用物理内存是510兆,总页文件是2.5G,这个是包含物理内存的页文件;可用页文件是1.9G。这里还标识了总进程空间,还有可用的进程空间,程序只用了22兆的内存空间。这里说的都是大约数。
内存繁忙程序是标识当前系统内存管理的繁忙程序,从0到100,其实用处不大。

·在函数里面静态分配一些内存后,看看究竟发生什么
char stat[65536];
MEMORYSTATUS memStatus1;
GlobalMemoryStatus(&memStatus1);
cout<<"静态分配空间:"<<endl;
printf("指针地址=%x"n",stat);
cout<<"减少物理内存="<<memStatus.dwAvailPhys-memStatus1.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus.dwAvailPageFile-memStatus1.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatus.dwAvailVirtual-             
memSta tus1.dwAvailVirtual<<endl<<endl;
结果如下:

 可以看出,物理内存、可用页文件和进程空间都没有损耗。因为局部变量是分配在线程堆栈里面的,每个线程系统都会建立一个默认1M大小的堆栈给线程函数调用使用。如果分配超过1M,就会出现堆栈溢出。
·在函数里面动态分配300M的内存后,看看究竟发生什么
char *dynamic=new char[300*1024*1024];
MEMORYSTATUS memStatus2;
GlobalMemoryStatus(&memStatus2);
cout<<"动态分配空间:"<<endl;
printf("指针地址=%x"n",dynamic);
cout<<"减少物理内存="<<memStatus.dwAvailPhys-memStatus2.dwAvailPhys<<endl;
cout<<"减少可用页文件="<<memStatus.dwAvailPageFile-memStatus2.dwAvailPageFile<<endl;
cout<<"减少可用进程空间="<<memStatus.dwAvailVirtual-memStatus2.dwAvailVirtual<<endl<<endl;
结果如下:

 动态分配情况下,系统分配直到内存页文件使用完为止,当然,系统要留一下系统使用的页面。

2.3 进程区域地址查询
在给定一个进程空间的地址后,可以查询它所在区域和相邻页面的状态,包括页面保护属性、存储器类型等。
·C++静态分配了两次内存,一次是4K大一点,一个是900K左右。
char arrayA[4097];
char arrayB[900000];
第一次查询:
long len=sizeof(MEMORY_BASIC_INFORMATION);
MEMORY_BASIC_INFORMATION mbiA;
VirtualQuery(arrayA,&mbiA,len);
cout<<"静态内存地址属性:"<<endl;
cout<<"区域基地址="<<mbiA.AllocationBase<<endl;
cout<<"区域邻近页面状态="<<mbiA.State<<endl;
cout<<"区域保护属性="<<mbiA.AllocationProtect<<endl;
cout<<"页面基地址="<<mbiA.BaseAddress<<endl;
printf("arrayA指针地址=%x"n",arrayA);
cout<<"从页面基地址开始的大小="<<mbiA.RegionSize<<endl;
cout<<"邻近页面物理存储器类型="<<mbiA.Type<<endl;
cout<<"页面保护属性="<<mbiA.Protect<<endl<<endl;
第二次查询:
MEMORY_BASIC_INFORMATION mbiB;
VirtualQuery(arrayB,&mbiB,len);
cout<<"静态内存地址属性:"<<endl;
cout<<"区域基地址="<<mbiB.AllocationBase<<endl;
cout<<"区域邻近页面状态="<<mbiB.State<<endl;
cout<<"区域保护属性="<<mbiB.AllocationProtect<<endl;
cout<<"页面基地址="<<mbiB.BaseAddress<<endl;
printf("arrayB指针地址=%x"n",arrayB);
cout<<"从页面基地址开始的大小="<<mbiB.RegionSize<<endl;
cout<<"邻近页面物理存储器类型="<<mbiB.Type<<endl;
cout<<"页面保护属性="<<mbiB.Protect<<endl<<endl;
说明:区域基地址指的是给定地址所在的进程空间区域;
邻近页面状态指的是与给定地址所在页面状态相同页面的属性:MEM_FREE(空闲=65536)、MEM_RESERVE(保留=8192)和MEM_COMMIT(提交=4096)。
区域保护属性指的是区域初次被保留时被赋予的保护属性:PAGE_READONLY(2)、PAGE_READWRITE(4)、PAGE_WRITECOPY(8)和PAGE_EXECUTE_WRITECOPY(128)等等。
页面基地址指的是给定地址所在页面的基地址。
从页面基地址开始的区域页面的大小,指的是与给定地址所在页面状态、保护属性相同的页面。
邻近页面物理存储器类型指的是与给定地址所在页面相同的存储器类型,包括:MEM_PRIVATE(页文件=131072)、MEM_MAPPED(文件映射=262144)和MEM_IMAGE(exe映像=16777216)。
页面保护属性指的是页面被指定的保护属性,在区域保护属性指定后更新。
结果如下:

 如前所说,这是在堆栈区域0x0004 0000里分配的,后分配的地址arrayB反而更小,符合堆栈的特性。arrayA和arrayB它们处于不同的页面。页面都受页文件支持,并且区域都是提交的,是系统在线程创建时提交的。
· C++动态分配了两次内存,一次是1K大一点,一个是64K左右。所以应该不会在一个区域。
char *dynamicA=new char[1024];
char *dynamicB=new char[65467];
VirtualQuery(dynamicA,&mbiA,len);
cout<<"动态内存地址属性:"<<endl;
cout<<"区域基地址="<<mbiA.AllocationBase<<endl;
cout<<"区域邻近页面状态="<<mbiA.State<<endl;
cout<<"区域保护属性="<<mbiA.AllocationProtect<<endl;
cout<<"页面基地址="<<mbiA.BaseAddress<<endl;
printf("dynamicA指针地址=%x"n",dynamicA);
cout<<"从页面基地址开始的大小="<<mbiA.RegionSize<<endl;
cout<<"邻近页面物理存储器类型="<<mbiA.Type<<endl;
cout<<"页面保护属性="<<mbiA.Protect<<endl<<endl;
VirtualQuery(dynamicB,&mbiB,len);
cout<<"动态内存地址属性:"<<endl;
cout<<"区域基地址="<<mbiB.AllocationBase<<endl;
cout<<"区域邻近页面状态="<<mbiB.State<<endl;
cout<<"区域保护属性="<<mbiB.AllocationProtect<<endl;
cout<<"页面基地址="<<mbiB.BaseAddress<<endl;
printf("dynamicB指针地址=%x"n",dynamicB);
cout<<"从页面基地址开始的大小="<<mbiB.RegionSize<<endl;
cout<<"邻近页面物理存储器类型="<<mbiB.Type<<endl;
cout<<"页面保护属性="<<mbiB.Protect<<endl;
结果如下:

 
这里是动态分配,dynamicA和dynamicB处于两个不同的区域;同样,页面都受页文件支持,并且区域都是提交的。
第二个区域是比64K大的,由分配粒度可知,区域至少是128K。那么,剩下的空间也是提交的吗,如果是的话那就太浪费了。看看就知道了:0x00E2 1000肯定在这个空间里,所以查询如下:
VirtualQuery((char*)0xE23390,&mbiB,len);
cout<<"动态内存地址属性:"<<endl;
cout<<"区域基地址="<<mbiB.AllocationBase<<endl;
cout<<"区域邻近页面状态="<<mbiB.State<<endl;
cout<<"区域保护属性="<<mbiB.AllocationProtect<<endl;
cout<<"页面基地址="<<mbiB.BaseAddress<<endl;
printf("dynamicB指针地址=%x"n",0xE21000);
cout<<"从页面基地址开始的大小="<<mbiB.RegionSize<<endl;
cout<<"邻近页面物理存储器类型="<<mbiB.Type<<endl;
cout<<"页面保护属性="<<mbiB.Protect<<endl;
结果如下:

 
可以看出,邻近页面状态为保留,还没提交,预料之中;0x00E1 0000 这个区域的大小可以计算出来:69632+978944=1024K。系统动态分配了1M的空间,就为了64K左右大小的空间。可能是为了使得下次有要求分配时时不用再分配了。

  • 大小: 7.6 KB
  • 大小: 13.1 KB
  • 大小: 7.3 KB
  • 大小: 8.9 KB
  • 大小: 29.5 KB
  • 大小: 30.2 KB
  • 大小: 15.6 KB
分享到:
评论

相关推荐

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    《visual c++ 2010入门经典(第5版)》针对visual c++ 2010版本进行了全面更新,介绍了最新的开发环境和如何使用visual c++构建现实世界中的应用程序。拥有本书,您就迈向了通往使用两种c++版本编写应用程序的成功之路...

    Visual C++ 2005入门经典--源代码及课后练习答案

     本书系编程语言先驱者Ivor Horton的经典之作,是学习C++编程最畅销的图书品种之一,不仅涵盖了Visual C++ .NET编程知识,还全面介绍了标准C++语言和.NET C++/CLI。本书延续了Ivor Horton讲解编程语言的独特方法,...

    C++MFC教程

    使用CString可不指明内存大小,CString会根据需要自行分配。下面介绍几个成员函数: GetLength 得到字符串长度 GetAt 得到指定位置处的字符 operator + 相当于strcat void Format( LPCTSTR lpszFormat, ... ); ...

    JAVA上百实例源码以及开源项目源代码

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    JAVA上百实例源码以及开源项目

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    Visual.C#2010从入门到精通

    本书介绍了如何使用Visual C++ 6.0编写Windows应用程序。全书内容全面、结构清晰、由浅入深、注重实用,并结合了大量的实例,以方便读者理解。本书既包含菜单、鼠标、键盘等基本操作,又包含图形编程、Internet程序...

    C#微软培训资料

    第二章 运行环境 全面了解.NET.12 2.1 .NET 结构.12 2.2 公用语言运行时环境与公用语言规范.13 2.3 开 发 工 具 .17 2.4 小 结 .19 第三章 编写第一个应用程序 .20 3.1 Welcome 程序 .20 3.2 代 码 分 ...

    asp.net知识库

    C++ 泛型编程系列讲座之实施 泛型技巧系列:简单类型选择器 C# 泛型简介 我眼中的C#2.0新功能特性 泛型技巧系列:避免基类及接口约束 New Article 不该用Generics实现Abstract Factory的理由 C#2.0-泛型 C#2.0-...

Global site tag (gtag.js) - Google Analytics