全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询
本文背景:
在編程中,很多Windows或C++的內存函數不知道有什么區別,更別談有效使用;根本的原因是,沒有清楚的理解操作系統的內存管理機制,本文企圖通過簡單的總結描述,結合實例來闡明這個機制。
本文目的:
對Windows內存管理機制了解清楚,有效的利用C++內存函數管理和使用內存。
本文內容:
本文一共有六節,由于篇幅較多,故按節發表。其他章節請看本人博客的Windows內存管理及C++內存分配實例(一)(三)(四)(五)和(六)。
2.??????內存狀態查詢函數
2.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。
?
2.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左右大小的空間。可能是為了使得下次有要求分配時時不用再分配了。
?
總結
以上是生活随笔為你收集整理的全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数组扩展
- 下一篇: s3c2440移植MQTT