日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

全面介绍Windows内存管理机制及C++内存分配实例(五):堆

發布時間:2025/3/15 c/c++ 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 全面介绍Windows内存管理机制及C++内存分配实例(五):堆 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文背景:

在編程中,很多Windows或C++的內存函數不知道有什么區別,更別談有效使用;根本的原因是,沒有清楚的理解操作系統的內存管理機制,本文企圖通過簡單的總結描述,結合實例來闡明這個機制。

本文目的:

對Windows內存管理機制了解清楚,有效的利用C++內存函數管理和使用內存。

本文內容:

本文一共有六節,由于篇幅較多,故按節發表。其他章節請看本人博客的Windows內存管理及C++內存分配實例(一)(二)(三)(四)和(六)。

?

?

5.??????內存管理機制--堆?(Heap)

·????????使用場合

堆是進程創建時在進程空間建立的區域,由堆管理器來管理。一個進程可以有很多個堆。進程有一個默認堆為1M,可以動態的擴大。

當程序需要管理很多小對象時,適合用堆;當需要的空間大于1M時,最好用虛擬內存來管理。

堆的優點是,有堆管理器來替它管理,不需管理具體的事情如頁面邊界

????????和分配粒度等問題,你可以從調用函數看的出來,比VirtualAlloc的參數少了

????????不少。

?????????堆的缺點是分配和釋放的速度比前幾種機制要慢,所以最好不要超過

?????????1M;不像虛擬內存那樣隨時提交和釋放,因為它是由堆管理器決定的。如果

?????????用堆分配1G的空間,需要1分種,而用虛擬內存,則感覺不到任何延遲。????

·????????默認堆

進程默認堆是供所有線程使用的,每當線程需要從堆中分配釋放內存區時,系

?統會同步堆,所以訪問速度較慢。

它的默認大小是1M,同樣的,你可以通過以下鏈接命令改變其大小:

#pragma comment(linker,"/HEAP:102400000,1024000")

第一個值是堆的保留空間,第二個值是堆開始時提交的物理內存大小。本文將堆改變為100M。

當你在程序中擴大了堆提交的物理內存時,進程運行時,物理內存將減少擴大的數量。但是,默認堆總是可以擴大的,不能限制它的最大值。

當你在程序中擴大了堆保留的空間時,進程運行時,可用進程空間將會減少擴大的數量。

每次你用New操作符分配內存時,進程空間會相應的減少,物理內存也會相應的減少。

?

一個重要的提示,本文經過測試,如果你需要的內存塊大部分都超過512K,那么,建堆時給它的初始大小不應該很大,因為,如果你所需內存塊大于512K的話,它不是從堆中分配的,也就是說不用堆中默認的空間,但其仍然屬于堆管理。

?

默認堆的一個用處是系統函數需要利用它運行。比如,Windows2000的字符集是UNICODE的,如果調用ANSI版本的函數,系統需要利用堆來從ANSI到UNICODE的轉換,調用UNICODE版本的函數。

·????????自建堆

ü??????使用場合

保護數據結構:

將不同的數據結構存在不同的堆中,可以防止不同的結構之間由于指針誤操作而破壞了它們。

消除內存碎片:

將大小不同的結構保存在一個堆中,會導致碎片的產生,比如釋放一個小結構時,大結構也不能利用它。

獨享堆的快速:

如果用默認堆的話,線程之間是同步訪問,速度慢;如果創建獨享堆,則系統可以不需同步,比較快。

第二個快速體現在釋放的快速,默認堆中,你只能釋放某個內存塊,而不能釋放整個堆;而獨享堆可以一次釋放堆,也就是釋放了所有的內存塊。

ü??????開始使用

?????????建立堆:

????????????使用以下API??

????????????HANDLE HeapCreate(DWORD?選項,SIZE_T?初始大小,SIZE_T?最大值)

“選項”?取值為0?,不是以下任意一個

HEAP_NO_SERIALIZE,系統無需同步堆

HEAP_GENERATE_EXCEPTIONS,當創建失敗或分配失敗時產生異常。

“初始大小”是堆的大小,系統會規整到頁面的整數倍,如0~4096的任何數都為4096;但是,進程空間至少要64K。

“最大值”是堆允許的最大值;為0則無限。

使用HEAP_NO_SERIALIZE需確定只有單線程訪問這個堆,否則有可能破壞堆;或程序有同步代碼來同步堆。

C++程序如下:

pHeap=(char*)GetProcessHeap();

printf("默認堆地址=%x/n",pHeap);

?

MEMORYSTATUS memStatus2;

????????????GlobalMemoryStatus(&memStatus2);

HANDLE hHeap=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*50,0);

????????????char* pHeap=(char*)hHeap;

????????????printf("新建堆1地址=%x/n",pHeap);??

????????????if(hHeap==NULL)

????????????{

????????????????????????cout<<"創建堆失敗!"<<endl;

????????????}

????????????MEMORYSTATUS memStatus3;

????????????GlobalMemoryStatus(&memStatus3);

????????????cout<<"建立堆后:"<<endl;

cout<<"減少物理內存="<<memStatus2.dwAvailPhys-memStatus3.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus2.dwAvailPageFile-memStatus3.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="<<memStatus2.dwAvailVirtual-memStatus3.dwAvailVirtual<<endl<<endl;

?

HANDLE hHeap2=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*10,0);

????????????char* pHeap2=(char*)hHeap2;

????????????printf("新建堆2地址=%x/n",pHeap2);

?

????????????結果如下:

????????????

當建立堆1時,它分配了50M的物理內存給堆使用;當建立堆2時,堆2的地址是0x04bc 0000=0x019c 0000+50*1024*1024.

????????分配內存:

????????????使用以下API

????????????PVOID HeapAlloc(HANDLE?堆句柄,DWORD?選項,SIZE_T?字節數)

????????????“選項”可以是,

????????????HEAP_ZERO_MEMORY,所有字節初始化為0

????????????HEAP_NO_SERIALIZE,堆這個內存區獨享

HEAP_GENERATE_EXCEPTIONS,產生異常。如果創建堆有了它就不用再設了。異常可能為:STATUS_NO_MEMOR(無足夠內存)和STATUS_ACCESS_VIOLATION(堆被破壞,分配失敗)。

?

C++程序如下:

GlobalMemoryStatus(&memStatus3);

PVOID pV=HeapAlloc(hHeap,

HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*507);

????????????if(pV==NULL)

????????????{

????????????????????????cout<<"分配堆內存失敗!"<<endl;

????????????}

????????????char?* pC=(char*)pV;

????????????printf("第一次分配地址=%x/n",pC);

???????????

????????????MEMORYSTATUS memStatus4;

????????????GlobalMemoryStatus(&memStatus4);

????????????cout<<"第一次堆分配后:"<<endl;

cout<<"減少物理內存="<<memStatus3.dwAvailPhys-memStatus4.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus3.dwAvailPageFile-memStatus4.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="<<memStatus3.dwAvailVirtual-memStatus4.dwAvailVirtual<<endl<<endl;

?

PVOID pV2=HeapAlloc(hHeap,

HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*508);

????????????if(pV2==NULL)

????????????{

????????????????????????cout<<"分配堆內存失敗!"<<endl;

????????????}

????????????char?* pC2=(char*)pV2;

????????????printf("第二次分配地址=%x/n",pC2);

????????????MEMORYSTATUS memStatus5;

????????????GlobalMemoryStatus(&memStatus5);

????????????cout<<"第二次堆分配后:"<<endl;

cout<<"減少物理內存="<<memStatus4.dwAvailPhys-memStatus5.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus4.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="<<memStatus4.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;

????????????for(int?i=0;i<200*1024;i++)

????????????????????????pC2[i]=9;

?

????????????MEMORYSTATUS memStatus10;

????????????GlobalMemoryStatus(&memStatus10);

????????????cout<<"第二次堆使用一半后:"<<endl;

cout<<"減少物理內存="<<memStatus5.dwAvailPhys-memStatus10.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus5.dwAvailPageFile-memStatus10.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="

<<memStatus5.dwAvailVirtual-memStatus10.dwAvailVirtual<<endl<<endl;

結果如下:

?

可以看出,第一次分配507K的地址為0x04ad d650<0x04bc 0000,它是在堆中分配的;第二次分配508K的地址為0x055c 0020>0x04bc 0000,它是在堆外分配的;無論在多大的堆中,只要分配內存塊大于507K時,都會在堆外分配,但是,它像在堆中一樣,存在堆的鏈接表中,受堆管理。分配時,系統使用的是虛擬頁文件;只有在真正使用時,才會分配物理內存。

至于為什么分配大于507K會在堆外分配而不直接使用堆中的內存,目前仍然不清楚。

?????????改變大小:

PVOID HeapReAlloc(HANDLE?堆句柄,DWORD?選項,PVOID?舊內存塊地址,SIZE_T?新內存塊大小)

“選項”除了以上三個外,還有HEAP_REALLOC_IN_PLACE_ONLY,指定不能移動原有內存塊的地址。

C++程序如下:

GlobalMemoryStatus(&memStatus4);

????????????PVOID pV2New=HeapReAlloc(hHeap,0,pV2,1024*1024*2);

????????????if(pV2New!=NULL)

????????????{

????????????char?* pC2New=(char*)pV2New;

????????????printf("改變分配地址=%x/n",pC2New);

????????????cout<<pC2New[0]<<endl;

????????????//cout<<pC2[0]<<endl;出現訪問違規

????????????SIZE_T lenNew=HeapSize(hHeap,0,pV2New);

????????????cout<<"改變后大小="<<lenNew<<endl;

????????????}

????????????GlobalMemoryStatus(&memStatus5);

????????????cout<<"改變分配后:"<<endl;

cout<<"減少物理內存="<<memStatus4.dwAvailPhys-memStatus5.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus4.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="

<<memStatus4.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;

結果如下:

?

可以看出,新內存塊緊接著原來內存塊結束的地方開始創建,大小為2M;原來的內存塊的內容被銷毀和釋放,所以新內存塊只減少了增加的內存量。一個缺點就是,新內存塊居然不保留原來內存的內容!另外,如果采用HEAP_REALLOC_IN_PLACE_ONLY的話,出現Not Enough Quote異常。也就是說,當前內存的狀況是,必須移動才可以擴大此內存塊。

?????????查詢內存:

????????????可以查詢堆中一個內存塊的大小。

????????????SIZE_T HeapSize(HANDLE?堆句柄,DWORD?選項,LPVOID?內存塊地址)

????????????“選項”可為0或HEAP_NO_SERIALIZE。

????????????參考以上例子。

?????????釋放內存塊:

????????????BOOL HeapFree(HANDLE?堆句柄,DWORD?選項,PVOID?內存塊地址)

????????????“選項”可為0或HEAP_NO_SERIALIZE。

????????????C++程序如下:

????????????GlobalMemoryStatus(&memStatus5);

????????????HeapFree(hHeap,0,pV2New);

????????????MEMORYSTATUS memStatus6;

????????????GlobalMemoryStatus(&memStatus6);

????????????cout<<"第二次堆分配釋放后:"<<endl;

cout<<"增加物理內存="<<memStatus6.dwAvailPhys-memStatus5.dwAvailPhys<<endl;

cout<<"增加可用頁文件="<<memStatus6.dwAvailPageFile-memStatus5.dwAvailPageFile<<endl;

cout<<"增加可用進程空間="<<memStatus6.dwAvailVirtual-memStatus5.dwAvailVirtual<<endl<<endl;

結果如下:

?

????????????內存空間釋放了原來的2M空間。

?????????釋放堆:

????????????BOOL HeapDestroy(HANDLE?堆句柄)

????????????不能用它釋放默認堆,系統忽略它的處理。

這一次,我們先在堆1中分配了70M的內存,由于它很大,所以,堆在堆外給它分配了內存,所以,堆1一共有50M+70M=120M。釋放程序如下:

PVOID pV4=HeapAlloc(hHeap,HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS)

,1024*1024*70);

????????????if(pV4==NULL)

????????????{

????????????????????????cout<<"分配堆內存失敗!"<<endl;

????????????}

????????????char?* pC4=(char*)pV4;

????????????printf("第四次堆分配=%x/n",pC4);

????????????MEMORYSTATUS memStatus9;

????????????GlobalMemoryStatus(&memStatus9);

????????????cout<<"分配堆內存后:"<<endl;

cout<<"減少物理內存="<<memStatus7.dwAvailPhys-memStatus9.dwAvailPhys<<endl;

cout<<"減少可用頁文件="<<memStatus7.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;

cout<<"減少可用進程空間="<<memStatus7.dwAvailVirtual-memStatus9.dwAvailVirtual<<endl<<endl;

?

????????????SIZE_T len=HeapSize(hHeap,0,pV4);

????????????cout<<"len="<<len<<endl;

????????????bool?re=HeapDestroy(hHeap);

????????????if(re==false)

????????????{

????????????????????????cout<<"釋放堆失敗!"<<endl;

}

MEMORYSTATUS memStatus8;

????????????GlobalMemoryStatus(&memStatus8);

????????????cout<<"釋放堆后:"<<endl;

cout<<"增加物理內存="<<memStatus8.dwAvailPhys-memStatus9.dwAvailPhys<<endl;

cout<<"增加可用頁文件="<<memStatus8.dwAvailPageFile-memStatus9.dwAvailPageFile<<endl;

cout<<"增加可用進程空間="<<memStatus8.dwAvailVirtual-memStatus9.dwAvailVirtual<<endl<<endl;

?

結果如下:

?

如所猜想一樣,釋放了120M內存。

?

?????????獲取所有堆:

????????????DWORD GetProcessHeaps(DWORD?數量,PHANDLE?句柄數組)

????????????“數量”是你想獲取的堆數目;

????????????“句柄數組”是獲得的堆句柄。

????????????默認堆也可以獲取。

????????????HANDLE????????handles[10];

????????????memset(handles,0,sizeof(handles));

????????????GetProcessHeaps(10,handles);

????????????for(int?i=0;i<10;i++)

????????????????????????cout<<"堆"<<i+1<<"="<<handles[i]<<endl;

????????????結果如下:

????????????

可以看見,一共有8個堆,堆1是默認堆,堆7和堆8是本文建立的堆。另外5個不知來源。

?????????驗證堆:

????????????BOOL HeapValidate(HANDLE?堆句柄,DWORD?選項,LPVOID?內存塊地址)

????????????“選項”?可為0或HEAP_NO_SERIALIZE;

????????????“內存塊地址”為NULL時,驗證所有內存塊。

????????????C++程序如下:

HANDLE????????handles[10];

????????????memset(handles,0,sizeof(handles));

????????????GetProcessHeaps(10,handles);

????????????for(int?i=0;i<10;i++)

????????????{

????????????????????????cout<<"堆"<<i+1<<"="<<handles[i]<<"???";

????????????????????????if(HeapValidate(handles[i],0,NULL))

????????????????????????????????????cout<<"驗證堆成功!"<<endl;

????????????????????????else

????????????????????????????????????cout<<endl;

?

????????????}

結果如下:

?

????????合并內存塊:

????????????UINT HeapCompact(HANDLE?堆句柄,DWORD?選項)

????????????“選項”?可為0或HEAP_NO_SERIALIZE;

????????????此函數可以合并空閑內存塊。

???????

????????其他函數:

????????????HeapLock和HeapUnlock?通常是系統使用的;

????????????HeapWalk可以遍歷堆內存,需要以上兩個函數。

???????????

·????????C++內存函數

Malloc和Free

這是C語言使用的函數,只能從默認堆中分配內存,并且只是分配內存,不能調用構造函數,且只是按字節分配,不能按類型分配。

New?和Delete

這是C++語言使用的函數,默認情況下從默認堆中分配內存,但是也可以通過重載New函數,從自建堆中按類型分配;同時可以執行構造函數和析構函數。它底層是通過HeapAlloc和HeapFree實現的。?依賴于編譯器的實現。

GlobalAlloc?和GlobalFree

這是比HeapAlloc和HeapFree更慢的函數,但是也沒有比它們更好的優點,只能在默認堆中分配;16位操作系統下利用它們分配內存。

LocalAlloc和LocalFree

在WindowsNT?內核里,和GlobalAlloc、GlobalFree是一樣的。

?

·????????一個例子

默認情況下,New關鍵字是利用HeapAlloc在默認堆上建立對象。本文重載了類的New方法,使得類在自己的堆中存放,這樣可以與外面的對象隔離,以免重要的數據結構被意外破壞。由于類中的成員變量是在堆中存放,因此不局限于線程堆棧的1M空間。

C++程序如下:

class?AllocateInOtherHeap

{

public:

????????????AllocateInOtherHeap(void);

????????????~AllocateInOtherHeap(void);

????????????void*?operator?new(size_t size);

????????????static?HANDLE heap;

?????????public:

????????????//類對象唯一所需的空間

????????????int?iArray[1024*1024*10];

????????????AllocateInOtherHeap::AllocateInOtherHeap(void)

{

????????????cout<<"AllocateInOtherHeap()"<<endl;

????????????//如果New函數沒有分配夠空間,那么此處會出現訪問違規

????????????memset(iArray,0,sizeof(AllocateInOtherHeap));

????????????iArray[1024]=8;

}

void* AllocateInOtherHeap::operator?new(size_t size)

{?????????

????????????if(heap==NULL)

heap=HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS,1024*1024*10,0);

????????????//分配足夠這個類對象的空間

????????????void* p=HeapAlloc(heap,0,sizeof(AllocateInOtherHeap));

????????????cout<<"堆的大小="<<HeapSize(heap,0,p)<<endl;

????????????printf("AllocateInOtherHeap堆地址=%x/n",heap);

????????????printf("AllocateInOtherHeap返回地址=%x/n",p);

????????????return?p;

}

AllocateInOtherHeap::~AllocateInOtherHeap(void)

{

????????????cout<<"~AllocateInOtherHeap"<<endl;

}

void?AllocateInOtherHeap::operator?delete(void* p)

{?????????

????????????HeapFree(heap,0,p);

????????????HeapDestroy(heap);

????????????cout<<"delete()"<<endl;??????????

}

};

結果如下:

可見,new函數先分配夠空間,然后才能初始化對象變量;而delete函數得先做析構,才能釋放空間。對象保存在堆外,因為大于512K;對象大小剛好是iArray變量的大小。

注意,如果沒有分配足夠的空間,雖然你可以得到對象指針,但是你訪問數據時可能會出現訪問違規,如果沒出現,那更慘,意味著你讀寫了別人的數據。

新人創作打卡挑戰賽發博客就能抽獎!定制產品紅包拿不停!

總結

以上是生活随笔為你收集整理的全面介绍Windows内存管理机制及C++内存分配实例(五):堆的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美精品videos极品 | 性高潮网站 | 国产91精| 大胸奶汁乳流奶水出来h | 邵氏电影《金莲外传2》免费观看 | 国产精品无码久久久久一区二区 | 丁香婷婷九月 | 可以看毛片的网站 | 天天做天天爱天天爽综合网 | 国产a免费 | 欧美亚洲一区 | 国产极品一区 | 欧美激情一区二区三级高清视频 | 伊人99热 | 国产日韩一区二区三免费高清 | 亚洲视频自拍偷拍 | 日韩人妻一区二区三区 | 伊人影院在线观看 | 黄色精品一区 | 91成人在线免费观看 | 中文在线字幕免费观看电 | 黄色网页网站 | 天天射视频 | 波多野结衣www | 欧美 日韩 国产 成人 在线 | 蘑菇av | 日韩一区二区影视 | 免费在线观看的av | 黄色精品视频在线观看 | 国产成人午夜精华液 | 国产91亚洲 | 麻豆国产91在线播放 | 92精品| 香蕉二区| 中文字幕有码在线视频 | 免费av免费看 | 九九热在线免费观看 | 久久香蕉国产 | 一区二区麻豆 | 欧洲亚洲激情 | 欧美在线不卡视频 | 蜜桃av影视 | 中文字幕1页 | 在线观看日韩 | 性激情视频 | 老司机av影院| 麻豆精品在线播放 | 午夜精品久久久久久久91蜜桃 | 国产精品一区二区白浆 | 亚洲欧美国产一区二区 | 欧美有码视频 | 人妻少妇精品无码专区二区 | 爽妇网国产精品 | av首页在线观看 | 好大好爽好舒服 | a在线| 午夜18视频在线观看 | 色婷在线 | 乖女从小调教h尿便器小说 欧美韩一区二区 | 一级爱爱免费视频 | 丰满少妇被猛烈进入一区二区 | 天天鲁一鲁摸一摸爽一爽 | 98视频在线 | 成人依人| 免费网站在线观看视频 | 在线成人免费视频 | 97人妻人人揉人人躁人人 | 国产综合久久久久 | 一区二区毛片 | 99超碰在线观看 | 超碰在线最新地址 | 又黄又爽网站 | 无码人妻丰满熟妇区96 | 91精品国产乱码久久久久久久久 | 91入囗| 91精品综合久久 | 亚洲成av人片在线观看无 | 久久大胆人体 | 在线日韩欧美 | 嫩操影院 | 成全影视在线观看第8季 | 国产1区二区 | 久久国产精品视频 | 丁香色综合 | 国产又爽又黄的视频 | 99国产精品一区二区三区 | 久久在现| 中文字幕欧美人妻精品一区蜜臀 | fc2ppv在线播放 | 欧美日韩一级二级三级 | 亚洲日b视频 | 成人精品视频一区二区三区尤物 | 午夜激情毛片 | 自拍愉拍 | 99久久精品无免国产免费 | 亚洲区一区二区 | 国产xxx69麻豆国语对白 | 豆花在线视频 | 成年人爱爱视频 |