全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询
本文背景:
在編程中,很多Windows或C++的內(nèi)存函數(shù)不知道有什么區(qū)別,更別談?dòng)行褂?#xff1b;根本的原因是,沒有清楚的理解操作系統(tǒng)的內(nèi)存管理機(jī)制,本文企圖通過簡(jiǎn)單的總結(jié)描述,結(jié)合實(shí)例來闡明這個(gè)機(jī)制。
本文目的:
對(duì)Windows內(nèi)存管理機(jī)制了解清楚,有效的利用C++內(nèi)存函數(shù)管理和使用內(nèi)存。
本文內(nèi)容:
本文一共有六節(jié),由于篇幅較多,故按節(jié)發(fā)表。其他章節(jié)請(qǐng)看本人博客的Windows內(nèi)存管理及C++內(nèi)存分配實(shí)例(一)(三)(四)(五)和(六)。
2.??????內(nèi)存狀態(tài)查詢函數(shù)
2.1系統(tǒng)信息
Windows?提供API可以查詢系統(tǒng)內(nèi)存的一些屬性,有時(shí)候我們需要獲取一些頁面大小、分配粒度等屬性,在分配內(nèi)存時(shí)用的上。
請(qǐng)看以下C++程序:
SYSTEM_INFO sysInfo;
????????????GetSystemInfo(&sysInfo);
????????????cout<<"機(jī)器屬性:"<<endl;
????????????cout<<"頁大小="<<sysInfo.dwPageSize<<endl;
????????????cout<<"分配粒度="<<sysInfo.dwAllocationGranularity<<endl;
????????????cout<<"用戶區(qū)最小值="<<sysInfo.lpMinimumApplicationAddress<<endl;
???cout<<"用戶區(qū)最大值="
<<sysInfo.lpMaximumApplicationAddress<<endl<<endl;
結(jié)果如下:
?
可以看出,頁面大小是4K,區(qū)域分配粒度是64K,進(jìn)程用戶區(qū)是0x0001 0000~0x7FFE FFFF。
?
2.2內(nèi)存狀態(tài)
·????????內(nèi)存狀態(tài)可以獲取總內(nèi)存和可用內(nèi)存,包括頁文件和物理內(nèi)存。
請(qǐng)看以下C++程序:
MEMORYSTATUS memStatus;
????????????GlobalMemoryStatus(&memStatus);
????????????cout<<"內(nèi)存初始狀態(tài):"<<endl;
????????????cout<<"內(nèi)存繁忙程度="<<memStatus.dwMemoryLoad<<endl;
????????????cout<<"總物理內(nèi)存="<<memStatus.dwTotalPhys<<endl;
????????????cout<<"可用物理內(nèi)存="<<memStatus.dwAvailPhys<<endl;
????????????cout<<"總頁文件="<<memStatus.dwTotalPageFile<<endl;
????????????cout<<"可用頁文件="<<memStatus.dwAvailPageFile<<endl;
????????????cout<<"總進(jìn)程空間="<<memStatus.dwTotalVirtual<<endl;
???cout<<"可用進(jìn)程空間="<<memStatus.dwAvailVirtual<<endl<<endl;
結(jié)果如下:
可以看出,總物理內(nèi)存是1G,可用物理內(nèi)存是510兆,總頁文件是2.5G,這個(gè)是包含物理內(nèi)存的頁文件;可用頁文件是1.9G。這里還標(biāo)識(shí)了總進(jìn)程空間,還有可用的進(jìn)程空間,程序只用了22兆的內(nèi)存空間。這里說的都是大約數(shù)。
內(nèi)存繁忙程序是標(biāo)識(shí)當(dāng)前系統(tǒng)內(nèi)存管理的繁忙程序,從0到100,其實(shí)用處不大。
?
·????????在函數(shù)里面靜態(tài)分配一些內(nèi)存后,看看究竟發(fā)生什么
char?stat[65536];
????????????MEMORYSTATUS memStatus1;
????????????GlobalMemoryStatus(&memStatus1);
????????????cout<<"靜態(tài)分配空間:"<<endl;
????????????printf("指針地址=%x/n",stat);
cout<<"減少物理內(nèi)存="<<memStatus.dwAvailPhys-memStatus1.dwAvailPhys<<endl;
cout<<"減少可用頁文件="<<memStatus.dwAvailPageFile-memStatus1.dwAvailPageFile<<endl;
cout<<"減少可用進(jìn)程空間="<<memStatus.dwAvailVirtual-??????????????
memSta tus1.dwAvailVirtual<<endl<<endl;
結(jié)果如下:
?
可以看出,物理內(nèi)存、可用頁文件和進(jìn)程空間都沒有損耗。因?yàn)榫植孔兞渴欠峙湓诰€程堆棧里面的,每個(gè)線程系統(tǒng)都會(huì)建立一個(gè)默認(rèn)1M大小的堆棧給線程函數(shù)調(diào)用使用。如果分配超過1M,就會(huì)出現(xiàn)堆棧溢出。
?
·????????在函數(shù)里面動(dòng)態(tài)分配300M的內(nèi)存后,看看究竟發(fā)生什么
char?*dynamic=new?char[300*1024*1024];
????????????MEMORYSTATUS memStatus2;
????????????GlobalMemoryStatus(&memStatus2);
????????????cout<<"動(dòng)態(tài)分配空間:"<<endl;
????????????printf("指針地址=%x/n",dynamic);
cout<<"減少物理內(nèi)存="<<memStatus.dwAvailPhys-memStatus2.dwAvailPhys<<endl;
cout<<"減少可用頁文件="<<memStatus.dwAvailPageFile-memStatus2.dwAvailPageFile<<endl;
cout<<"減少可用進(jìn)程空間="<<memStatus.dwAvailVirtual-memStatus2.dwAvailVirtual<<endl<<endl;
結(jié)果如下:
?
動(dòng)態(tài)分配情況下,系統(tǒng)分配直到內(nèi)存頁文件使用完為止,當(dāng)然,系統(tǒng)要留一下系統(tǒng)使用的頁面。
?
2.3?進(jìn)程區(qū)域地址查詢
在給定一個(gè)進(jìn)程空間的地址后,可以查詢它所在區(qū)域和相鄰頁面的狀態(tài),包括頁面保護(hù)屬性、存儲(chǔ)器類型等。
·????????C++靜態(tài)分配了兩次內(nèi)存,一次是4K大一點(diǎn),一個(gè)是900K左右。
char?arrayA[4097];
????????????char?arrayB[900000];
第一次查詢:
????????????long?len=sizeof(MEMORY_BASIC_INFORMATION);
????????????MEMORY_BASIC_INFORMATION mbiA;
????????????VirtualQuery(arrayA,&mbiA,len);
????????????cout<<"靜態(tài)內(nèi)存地址屬性:"<<endl;
????????????cout<<"區(qū)域基地址="<<mbiA.AllocationBase<<endl;
????????????cout<<"區(qū)域鄰近頁面狀態(tài)="<<mbiA.State<<endl;
????????????cout<<"區(qū)域保護(hù)屬性="<<mbiA.AllocationProtect<<endl;
????????????cout<<"頁面基地址="<<mbiA.BaseAddress<<endl;
????????????printf("arrayA指針地址=%x/n",arrayA);
????????????cout<<"從頁面基地址開始的大小="<<mbiA.RegionSize<<endl;
????????????cout<<"鄰近頁面物理存儲(chǔ)器類型="<<mbiA.Type<<endl;
????????????cout<<"頁面保護(hù)屬性="<<mbiA.Protect<<endl<<endl;
第二次查詢:
????????????MEMORY_BASIC_INFORMATION mbiB;
????????????VirtualQuery(arrayB,&mbiB,len);
????????????cout<<"靜態(tài)內(nèi)存地址屬性:"<<endl;
????????????cout<<"區(qū)域基地址="<<mbiB.AllocationBase<<endl;
????????????cout<<"區(qū)域鄰近頁面狀態(tài)="<<mbiB.State<<endl;
????????????cout<<"區(qū)域保護(hù)屬性="<<mbiB.AllocationProtect<<endl;
????????????cout<<"頁面基地址="<<mbiB.BaseAddress<<endl;
????????????printf("arrayB指針地址=%x/n",arrayB);
????????????cout<<"從頁面基地址開始的大小="<<mbiB.RegionSize<<endl;
????????????cout<<"鄰近頁面物理存儲(chǔ)器類型="<<mbiB.Type<<endl;
???cout<<"頁面保護(hù)屬性="<<mbiB.Protect<<endl<<endl;
?
說明:區(qū)域基地址指的是給定地址所在的進(jìn)程空間區(qū)域;
鄰近頁面狀態(tài)指的是與給定地址所在頁面狀態(tài)相同頁面的屬性:MEM_FREE(空閑=65536)、MEM_RESERVE(保留=8192)和MEM_COMMIT(提交=4096)。
區(qū)域保護(hù)屬性指的是區(qū)域初次被保留時(shí)被賦予的保護(hù)屬性:PAGE_READONLY(2)、PAGE_READWRITE(4)、PAGE_WRITECOPY(8)和PAGE_EXECUTE_WRITECOPY(128)等等。
頁面基地址指的是給定地址所在頁面的基地址。
從頁面基地址開始的區(qū)域頁面的大小,指的是與給定地址所在頁面狀態(tài)、保護(hù)屬性相同的頁面。
鄰近頁面物理存儲(chǔ)器類型指的是與給定地址所在頁面相同的存儲(chǔ)器類型,包括:MEM_PRIVATE(頁文件=131072)、MEM_MAPPED(文件映射=262144)和MEM_IMAGE(exe映像=16777216)。
頁面保護(hù)屬性指的是頁面被指定的保護(hù)屬性,在區(qū)域保護(hù)屬性指定后更新。
?
結(jié)果如下:
?
如前所說,這是在堆棧區(qū)域0x0004 0000里分配的,后分配的地址arrayB反而更小,符合堆棧的特性。arrayA和arrayB它們處于不同的頁面。頁面都受頁文件支持,并且區(qū)域都是提交的,是系統(tǒng)在線程創(chuàng)建時(shí)提交的。
?
·????????C++動(dòng)態(tài)分配了兩次內(nèi)存,一次是1K大一點(diǎn),一個(gè)是64K左右。所以應(yīng)該不會(huì)在一個(gè)區(qū)域。
char?*dynamicA=new?char[1024];
????????????char?*dynamicB=new?char[65467];
????????????VirtualQuery(dynamicA,&mbiA,len);
????????????cout<<"動(dòng)態(tài)內(nèi)存地址屬性:"<<endl;
????????????cout<<"區(qū)域基地址="<<mbiA.AllocationBase<<endl;
????????????cout<<"區(qū)域鄰近頁面狀態(tài)="<<mbiA.State<<endl;
????????????cout<<"區(qū)域保護(hù)屬性="<<mbiA.AllocationProtect<<endl;
????????????cout<<"頁面基地址="<<mbiA.BaseAddress<<endl;
????????????printf("dynamicA指針地址=%x/n",dynamicA);
????????????cout<<"從頁面基地址開始的大小="<<mbiA.RegionSize<<endl;
????????????cout<<"鄰近頁面物理存儲(chǔ)器類型="<<mbiA.Type<<endl;
????????????cout<<"頁面保護(hù)屬性="<<mbiA.Protect<<endl<<endl;
?
????????????VirtualQuery(dynamicB,&mbiB,len);
????????????cout<<"動(dòng)態(tài)內(nèi)存地址屬性:"<<endl;
????????????cout<<"區(qū)域基地址="<<mbiB.AllocationBase<<endl;
????????????cout<<"區(qū)域鄰近頁面狀態(tài)="<<mbiB.State<<endl;
????????????cout<<"區(qū)域保護(hù)屬性="<<mbiB.AllocationProtect<<endl;
????????????cout<<"頁面基地址="<<mbiB.BaseAddress<<endl;
????????????printf("dynamicB指針地址=%x/n",dynamicB);
????????????cout<<"從頁面基地址開始的大小="<<mbiB.RegionSize<<endl;
????????????cout<<"鄰近頁面物理存儲(chǔ)器類型="<<mbiB.Type<<endl;
????????????cout<<"頁面保護(hù)屬性="<<mbiB.Protect<<endl;
??
結(jié)果如下:
?
這里是動(dòng)態(tài)分配,dynamicA和dynamicB處于兩個(gè)不同的區(qū)域;同樣,頁面都受頁文件支持,并且區(qū)域都是提交的。
第二個(gè)區(qū)域是比64K大的,由分配粒度可知,區(qū)域至少是128K。那么,剩下的空間也是提交的嗎,如果是的話那就太浪費(fèi)了??纯淳椭懒?#xff1a;0x00E2 1000肯定在這個(gè)空間里,所以查詢?nèi)缦?#xff1a;
VirtualQuery((char*)0xE23390,&mbiB,len);
????????????cout<<"動(dòng)態(tài)內(nèi)存地址屬性:"<<endl;
????????????cout<<"區(qū)域基地址="<<mbiB.AllocationBase<<endl;
????????????cout<<"區(qū)域鄰近頁面狀態(tài)="<<mbiB.State<<endl;
????????????cout<<"區(qū)域保護(hù)屬性="<<mbiB.AllocationProtect<<endl;
????????????cout<<"頁面基地址="<<mbiB.BaseAddress<<endl;
????????????printf("dynamicB指針地址=%x/n",0xE21000);
????????????cout<<"從頁面基地址開始的大小="<<mbiB.RegionSize<<endl;
????????????cout<<"鄰近頁面物理存儲(chǔ)器類型="<<mbiB.Type<<endl;
???cout<<"頁面保護(hù)屬性="<<mbiB.Protect<<endl;
結(jié)果如下:
可以看出,鄰近頁面狀態(tài)為保留,還沒提交,預(yù)料之中;0x00E1 0000?這個(gè)區(qū)域的大小可以計(jì)算出來:69632+978944=1024K。系統(tǒng)動(dòng)態(tài)分配了1M的空間,就為了64K左右大小的空間??赡苁菫榱耸沟孟麓斡幸蠓峙鋾r(shí)時(shí)不用再分配了。
?
總結(jié)
以上是生活随笔為你收集整理的全面介绍Windows内存管理机制及C++内存分配实例(二):内存状态查询的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数组扩展
- 下一篇: hdu 6127---Hard cha