欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
堆和棧的區別
堆和棧的區別
一、預備知識—程序的內存分配
一個(gè)由c/C++編譯的程序占用的內存分為以下幾個(gè)部分
1、棧區(stack)— 由編譯器自動(dòng)分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類(lèi)似于數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時(shí)可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類(lèi)似于鏈表,呵呵。
3、全局區(靜態(tài)區)(static)—,全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區域。 - 程序結束后有系統釋放 
4、文字常量區—常量字符串就是放在這里的。 程序結束后由系統釋放
5、程序代碼區—存放函數體的二進(jìn)制代碼。
二、例子程序 
這是一個(gè)前輩寫(xiě)的,非常詳細 
//main.cpp 
int a = 0; 全局初始化區 
char *p1; 全局未初始化區 
main() 

int b; 棧 
char s[] = "abc"; 棧 
char *p2; 棧 
char *p3 = "123456"; 123456\0在常量區,p3在棧上。 
static int c =0; 全局(靜態(tài))初始化區 
p1 = (char *)malloc(10); 
p2 = (char *)malloc(20); 
分配得來(lái)得10和20字節的區域就在堆區。 
strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會(huì )將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。 

 


二、堆和棧的理論知識 
2.1申請方式 
stack: 
由系統自動(dòng)分配。 例如,聲明在函數中一個(gè)局部變量 int b; 系統自動(dòng)在棧中為b開(kāi)辟空間 
heap: 
需要程序員自己申請,并指明大小,在c中malloc函數 
如p1 = (char *)malloc(10); 
在C++中用new運算符 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在棧中的。 


2.2 
申請后系統的響應 
棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。 
堆:首先應該知道操作系統有一個(gè)記錄空閑內存地址的鏈表,當系統收到程序的申請時(shí), 
會(huì )遍歷該鏈表,尋找第一個(gè)空間大于所申請空間的堆結點(diǎn),然后將該結點(diǎn)從空閑結點(diǎn)鏈表中刪除,并將該結點(diǎn)的空間分配給程序,另外,對于大多數系統,會(huì )在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語(yǔ)句才能正確的釋放本內存空間。另外,由于找到的堆結點(diǎn)的大小不一定正好等于申請的大小,系統會(huì )自動(dòng)的將多余的那部分重新放入空閑鏈表中。 

2.3申請大小的限制 
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話(huà)的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M(也有的說(shuō)是1M,總之是一個(gè)編譯時(shí)就確定的常數),如果申請的空間超過(guò)棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。 
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來(lái)存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見(jiàn),堆獲得的空間比較靈活,也比較大。 


2.4申請效率的比較: 
棧由系統自動(dòng)分配,速度較快。但程序員是無(wú)法控制的。 
堆是由new分配的內存,一般速度比較慢,而且容易產(chǎn)生內存碎片,不過(guò)用起來(lái)最方便. 
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進(jìn)程的地址空間中保留一快內存,雖然用起來(lái)最不方便。但是速度快,也最靈活。 

2.5堆和棧中的存儲內容 
棧: 在函數調用時(shí),第一個(gè)進(jìn)棧的是主函數中后的下一條指令(函數調用語(yǔ)句的下一條可執行語(yǔ)句)的地址,然后是函數的各個(gè)參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態(tài)變量是不入棧的。 
當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開(kāi)始存的地址,也就是主函數中的下一條指令,程序由該點(diǎn)繼續運行。 
堆:一般是在堆的頭部用一個(gè)字節存放堆的大小。堆中的具體內容有程序員安排。 

2.6存取效率的比較 

char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在運行時(shí)刻賦值的; 
而bbbbbbbbbbb是在編譯時(shí)就確定的; 
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。 
比如: 
#include 
void main() 

char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 

對應的匯編代碼 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 
第一種在讀取時(shí)直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。 


2.7小結: 
堆和棧的區別可以用如下的比喻來(lái)看出: 
使用棧就象我們去飯館里吃飯,只管點(diǎn)菜(發(fā)出申請)、付錢(qián)、和吃(使用),吃飽了就走,不必理會(huì )切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 
使用堆就象是自己動(dòng)手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。 



windows進(jìn)程中的內存結構


在閱讀本文之前,如果你連堆棧是什么多不知道的話(huà),請先閱讀文章后面的基礎知識。 

接觸過(guò)編程的人都知道,高級語(yǔ)言都能通過(guò)變量名來(lái)訪(fǎng)問(wèn)內存中的數據。那么這些變量在內存中是如何存放的呢?程序又是如何使用這些變量的呢?下面就會(huì )對此進(jìn)行深入的討論。下文中的C語(yǔ)言代碼如沒(méi)有特別聲明,默認都使用VC編譯的release版。 

首先,來(lái)了解一下 C 語(yǔ)言的變量是如何在內存分部的。C 語(yǔ)言有全局變量(Global)、本地變量(Local),靜態(tài)變量(Static)、寄存器變量(Regeister)。每種變量都有不同的分配方式。先來(lái)看下面這段代碼: 

#include <stdio.h> 

int g1=0, g2=0, g3=0; 

int main() 

static int s1=0, s2=0, s3=0; 
int v1=0, v2=0, v3=0; 

//打印出各個(gè)變量的內存地址 

printf("0x%08x\n",&v1); //打印各本地變量的內存地址 
printf("0x%08x\n",&v2); 
printf("0x%08x\n\n",&v3); 
printf("0x%08x\n",&g1); //打印各全局變量的內存地址 
printf("0x%08x\n",&g2); 
printf("0x%08x\n\n",&g3); 
printf("0x%08x\n",&s1); //打印各靜態(tài)變量的內存地址 
printf("0x%08x\n",&s2); 
printf("0x%08x\n\n",&s3); 
return 0; 

編譯后的執行結果是: 

0x0012ff78 
0x0012ff7c 
0x0012ff80 

0x004068d0 
0x004068d4 
0x004068d8 

0x004068dc 
0x004068e0 
0x004068e4 

輸出的結果就是變量的內存地址。其中v1,v2,v3是本地變量,g1,g2,g3是全局變量,s1,s2,s3是靜態(tài)變量。你可以看到這些變量在內存是連續分布的,但是本地變量和全局變量分配的內存地址差了十萬(wàn)八千里,而全局變量和靜態(tài)變量分配的內存是連續的。這是因為本地變量和全局/靜態(tài)變量是分配在不同類(lèi)型的內存區域中的結果。對于一個(gè)進(jìn)程的內存空間而言,可以在邏輯上分成3個(gè)部份:代碼區,靜態(tài)數據區和動(dòng)態(tài)數據區。動(dòng)態(tài)數據區一般就是“堆棧”。“棧(stack)”和“堆(heap)”是兩種不同的動(dòng)態(tài)數據區,棧是一種線(xiàn)性結構,堆是一種鏈式結構。進(jìn)程的每個(gè)線(xiàn)程都有私有的“棧”,所以每個(gè)線(xiàn)程雖然代碼一樣,但本地變量的數據都是互不干擾。一個(gè)堆??梢酝ㄟ^(guò)“基地址”和“棧頂”地址來(lái)描述。全局變量和靜態(tài)變量分配在靜態(tài)數據區,本地變量分配在動(dòng)態(tài)數據區,即堆棧中。程序通過(guò)堆棧的基地址和偏移量來(lái)訪(fǎng)問(wèn)本地變量。 


├———————┤低端內存區域 
│ …… │ 
├———————┤ 
│ 動(dòng)態(tài)數據區 │ 
├———————┤ 
│ …… │ 
├———————┤ 
│ 代碼區 │ 
├———————┤ 
│ 靜態(tài)數據區 │ 
├———————┤ 
│ …… │ 
├———————┤高端內存區域 


堆棧是一個(gè)先進(jìn)后出的數據結構,棧頂地址總是小于等于棧的基地址。我們可以先了解一下函數調用的過(guò)程,以便對堆棧在程序中的作用有更深入的了解。不同的語(yǔ)言有不同的函數調用規定,這些因素有參數的壓入規則和堆棧的平衡。windows API的調用規則和ANSI C的函數調用規則是不一樣的,前者由被調函數調整堆棧,后者由調用者調整堆棧。兩者通過(guò)“__stdcall”和“__cdecl”前綴區分。先看下面這段代碼: 

#include <stdio.h> 

void __stdcall func(int param1,int param2,int param3) 

int var1=param1; 
int var2=param2; 
int var3=param3; 
printf("0x%08x\n",¶m1); //打印出各個(gè)變量的內存地址 
printf("0x%08x\n",¶m2); 
printf("0x%08x\n\n",¶m3); 
printf("0x%08x\n",&var1); 
printf("0x%08x\n",&var2); 
printf("0x%08x\n\n",&var3); 
return; 

int main() 

func(1,2,3); 
return 0; 

編譯后的執行結果是: 

0x0012ff78 
0x0012ff7c 
0x0012ff80 

0x0012ff68 
0x0012ff6c 
0x0012ff70 


├———————┤<—函數執行時(shí)的棧頂(ESP)、低端內存區域 
│ …… │ 
├———————┤ 
│ var 1 │ 
├———————┤ 
│ var 2 │ 
├———————┤ 
│ var 3 │ 
├———————┤ 
│ RET │ 
├———————┤<—“__cdecl”函數返回后的棧頂(ESP) 
│ parameter 1 │ 
├———————┤ 
│ parameter 2 │ 
├———————┤ 
│ parameter 3 │ 
├———————┤<—“__stdcall”函數返回后的棧頂(ESP) 
│ …… │ 
├———————┤<—棧底(基地址 EBP)、高端內存區域 


上圖就是函數調用過(guò)程中堆棧的樣子了。首先,三個(gè)參數以從又到左的次序壓入堆棧,先壓“param3”,再壓“param2”,最后壓入“param1”;然后壓入函數的返回地址(RET),接著(zhù)跳轉到函數地址接著(zhù)執行(這里要補充一點(diǎn),介紹UNIX下的緩沖溢出原理的文章中都提到在壓入RET后,繼續壓入當前EBP,然后用當前ESP代替EBP。然而,有一篇介紹windows下函數調用的文章中說(shuō),在windows下的函數調用也有這一步驟,但根據我的實(shí)際調試,并未發(fā)現這一步,這還可以從param3和var1之間只有4字節的間隙這點(diǎn)看出來(lái));第三步,將棧頂(ESP)減去一個(gè)數,為本地變量分配內存空間,上例中是減去12字節(ESP=ESP-3*4,每個(gè)int變量占用4個(gè)字節);接著(zhù)就初始化本地變量的內存空間。由于“__stdcall”調用由被調函數調整堆棧,所以在函數返回前要恢復堆棧,先回收本地變量占用的內存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前壓入參數占用的內存(ESP=ESP+3*4),繼續執行調用者的代碼。參見(jiàn)下列匯編代碼: 

;--------------func 函數的匯編代碼------------------- 

:00401000 83EC0C sub esp, 0000000C //創(chuàng )建本地變量的內存空間 
:00401003 8B442410 mov eax, dword ptr [esp+10] 
:00401007 8B4C2414 mov ecx, dword ptr [esp+14] 
:0040100B 8B542418 mov edx, dword ptr [esp+18] 
:0040100F 89442400 mov dword ptr [esp], eax 
:00401013 8D442410 lea eax, dword ptr [esp+10] 
:00401017 894C2404 mov dword ptr [esp+04], ecx 

……………………(省略若干代碼) 

:00401075 83C43C add esp, 0000003C ;恢復堆棧,回收本地變量的內存空間 
:00401078 C3 ret 000C ;函數返回,恢復參數占用的內存空間 
;如果是“__cdecl”的話(huà),這里是“ret”,堆棧將由調用者恢復 

;-------------------函數結束------------------------- 


;--------------主程序調用func函數的代碼-------------- 

:00401080 6A03 push 00000003 //壓入參數param3 
:00401082 6A02 push 00000002 //壓入參數param2 
:00401084 6A01 push 00000001 //壓入參數param1 
:00401086 E875FFFFFF call 00401000 //調用func函數 
;如果是“__cdecl”的話(huà),將在這里恢復堆棧,“add esp, 0000000C” 

聰明的讀者看到這里,差不多就明白緩沖溢出的原理了。先來(lái)看下面的代碼: 

#include <stdio.h> 
#include <string.h> 

void __stdcall func() 

char lpBuff[8]="\0"; 
strcat(lpBuff,"AAAAAAAAAAA"); 
return; 

int main() 

func(); 
return 0; 

編譯后執行一下回怎么樣?哈,“"0x00414141"指令引用的"0x00000000"內存。該內存不能為"read"。”,“非法操作”嘍!"41"就是"A"的16進(jìn)制的ASCII碼了,那明顯就是strcat這句出的問(wèn)題了。"lpBuff"的大小只有8字節,算進(jìn)結尾的\0,那strcat最多只能寫(xiě)入7個(gè)"A",但程序實(shí)際寫(xiě)入了11個(gè)"A"外加1個(gè)\0。再來(lái)看看上面那幅圖,多出來(lái)的4個(gè)字節正好覆蓋了RET的所在的內存空間,導致函數返回到一個(gè)錯誤的內存地址,執行了錯誤的指令。如果能精心構造這個(gè)字符串,使它分成三部分,前一部份僅僅是填充的無(wú)意義數據以達到溢出的目的,接著(zhù)是一個(gè)覆蓋RET的數據,緊接著(zhù)是一段shellcode,那只要著(zhù)個(gè)RET地址能指向這段shellcode的第一個(gè)指令,那函數返回時(shí)就能執行shellcode了。但是軟件的不同版本和不同的運行環(huán)境都可能影響這段shellcode在內存中的位置,那么要構造這個(gè)RET是十分困難的。一般都在RET和shellcode之間填充大量的NOP指令,使得exploit有更強的通用性。 


├———————┤<—低端內存區域 
│ …… │ 
├———————┤<—由exploit填入數據的開(kāi)始 
│ │ 
│ buffer │<—填入無(wú)用的數據 
│ │ 
├———————┤ 
│ RET │<—指向shellcode,或NOP指令的范圍 
├———————┤ 
│ NOP │ 
│ …… │<—填入的NOP指令,是RET可指向的范圍 
│ NOP │ 
├———————┤ 
│ │ 
│ shellcode │ 
│ │ 
├———————┤<—由exploit填入數據的結束 
│ …… │ 
├———————┤<—高端內存區域 


windows下的動(dòng)態(tài)數據除了可存放在棧中,還可以存放在堆中。了解C++的朋友都知道,C++可以使用new關(guān)鍵字來(lái)動(dòng)態(tài)分配內存。來(lái)看下面的C++代碼: 

#include <stdio.h> 
#include <iostream.h> 
#include <windows.h> 

void func() 

char *buffer=new char[128]; 
char bufflocal[128]; 
static char buffstatic[128]; 
printf("0x%08x\n",buffer); //打印堆中變量的內存地址 
printf("0x%08x\n",bufflocal); //打印本地變量的內存地址 
printf("0x%08x\n",buffstatic); //打印靜態(tài)變量的內存地址 

void main() 

func(); 
return; 

程序執行結果為: 

0x004107d0 
0x0012ff04 
0x004068c0 

可以發(fā)現用new關(guān)鍵字分配的內存即不在棧中,也不在靜態(tài)數據區。VC編譯器是通過(guò)windows下的“堆(heap)”來(lái)實(shí)現new關(guān)鍵字的內存動(dòng)態(tài)分配。在講“堆”之前,先來(lái)了解一下和“堆”有關(guān)的幾個(gè)API函數: 

HeapAlloc 在堆中申請內存空間 
HeapCreate 創(chuàng )建一個(gè)新的堆對象 
HeapDestroy 銷(xiāo)毀一個(gè)堆對象 
HeapFree 釋放申請的內存 
HeapWalk 枚舉堆對象的所有內存塊 
GetProcessHeap 取得進(jìn)程的默認堆對象 
GetProcessHeaps 取得進(jìn)程所有的堆對象 
LocalAlloc 
GlobalAlloc 

當進(jìn)程初始化時(shí),系統會(huì )自動(dòng)為進(jìn)程創(chuàng )建一個(gè)默認堆,這個(gè)堆默認所占內存的大小為1M。堆對象由系統進(jìn)行管理,它在內存中以鏈式結構存在。通過(guò)下面的代碼可以通過(guò)堆動(dòng)態(tài)申請內存空間: 

HANDLE hHeap=GetProcessHeap(); 
char *buff=HeapAlloc(hHeap,0,8); 

其中hHeap是堆對象的句柄,buff是指向申請的內存空間的地址。那這個(gè)hHeap究竟是什么呢?它的值有什么意義嗎?看看下面這段代碼吧: 

#pragma comment(linker,"/entry:main") //定義程序的入口 
#include <windows.h> 

_CRTIMP int (__cdecl *printf)(const char *, ...); //定義STL函數printf 
/*--------------------------------------------------------------------------- 
寫(xiě)到這里,我們順便來(lái)復習一下前面所講的知識: 
(*注)printf函數是C語(yǔ)言的標準函數庫中函數,VC的標準函數庫由msvcrt.dll模塊實(shí)現。 
由函數定義可見(jiàn),printf的參數個(gè)數是可變的,函數內部無(wú)法預先知道調用者壓入的參數個(gè)數,函數只能通過(guò)分析第一個(gè)參數字符串的格式來(lái)獲得壓入參數的信息,由于這里參數的個(gè)數是動(dòng)態(tài)的,所以必須由調用者來(lái)平衡堆棧,這里便使用了__cdecl調用規則。BTW,Windows系統的API函數基本上是__stdcall調用形式,只有一個(gè)API例外,那就是wsprintf,它使用__cdecl調用規則,同printf函數一樣,這是由于它的參數個(gè)數是可變的緣故。 
---------------------------------------------------------------------------*/ 
void main() 

HANDLE hHeap=GetProcessHeap(); 
char *buff=HeapAlloc(hHeap,0,0x10); 
char *buff2=HeapAlloc(hHeap,0,0x10); 
HMODULE hMsvcrt=LoadLibrary("msvcrt.dll"); 
printf=(void *)GetProcAddress(hMsvcrt,"printf"); 
printf("0x%08x\n",hHeap); 
printf("0x%08x\n",buff); 
printf("0x%08x\n\n",buff2); 

執行結果為: 

0x00130000 
0x00133100 
0x00133118 

hHeap的值怎么和那個(gè)buff的值那么接近呢?其實(shí)hHeap這個(gè)句柄就是指向HEAP首部的地址。在進(jìn)程的用戶(hù)區存著(zhù)一個(gè)叫PEB(進(jìn)程環(huán)境塊)的結構,這個(gè)結構中存放著(zhù)一些有關(guān)進(jìn)程的重要信息,其中在PEB首地址偏移0x18處存放的ProcessHeap就是進(jìn)程默認堆的地址,而偏移0x90處存放了指向進(jìn)程所有堆的地址列表的指針。windows有很多API都使用進(jìn)程的默認堆來(lái)存放動(dòng)態(tài)數據,如windows 2000下的所有ANSI版本的函數都是在默認堆中申請內存來(lái)轉換ANSI字符串到Unicode字符串的。對一個(gè)堆的訪(fǎng)問(wèn)是順序進(jìn)行的,同一時(shí)刻只能有一個(gè)線(xiàn)程訪(fǎng)問(wèn)堆中的數據,當多個(gè)線(xiàn)程同時(shí)有訪(fǎng)問(wèn)要求時(shí),只能排隊等待,這樣便造成程序執行效率下降。 

最后來(lái)說(shuō)說(shuō)內存中的數據對齊。所位數據對齊,是指數據所在的內存地址必須是該數據長(cháng)度的整數倍,DWORD數據的內存起始地址能被4除盡,WORD數據的內存起始地址能被2除盡,x86 CPU能直接訪(fǎng)問(wèn)對齊的數據,當他試圖訪(fǎng)問(wèn)一個(gè)未對齊的數據時(shí),會(huì )在內部進(jìn)行一系列的調整,這些調整對于程序來(lái)說(shuō)是透明的,但是會(huì )降低運行速度,所以編譯器在編譯程序時(shí)會(huì )盡量保證數據對齊。同樣一段代碼,我們來(lái)看看用VC、Dev-C++和lcc三個(gè)不同編譯器編譯出來(lái)的程序的執行結果: 

#include <stdio.h> 

int main() 

int a; 
char b; 
int c; 
printf("0x%08x\n",&a); 
printf("0x%08x\n",&b); 
printf("0x%08x\n",&c); 
return 0; 

這是用VC編譯后的執行結果: 
0x0012ff7c 
0x0012ff7b 
0x0012ff80 
變量在內存中的順序:b(1字節)-a(4字節)-c(4字節)。 

這是用Dev-C++編譯后的執行結果: 
0x0022ff7c 
0x0022ff7b 
0x0022ff74 
變量在內存中的順序:c(4字節)-中間相隔3字節-b(占1字節)-a(4字節)。 

這是用lcc編譯后的執行結果: 
0x0012ff6c 
0x0012ff6b 
0x0012ff64 
變量在內存中的順序:同上。 

三個(gè)編譯器都做到了數據對齊,但是后兩個(gè)編譯器顯然沒(méi)VC“聰明”,讓一個(gè)char占了4字節,浪費內存哦。 


基礎知識: 
堆棧是一種簡(jiǎn)單的數據結構,是一種只允許在其一端進(jìn)行插入或刪除的線(xiàn)性表。允許插入或刪除操作的一端稱(chēng)為棧頂,另一端稱(chēng)為棧底,對堆棧的插入和刪除操作被稱(chēng)為入棧和出棧。有一組CPU指令可以實(shí)現對進(jìn)程的內存實(shí)現堆棧訪(fǎng)問(wèn)。其中,POP指令實(shí)現出棧操作,PUSH指令實(shí)現入棧操作。CPU的ESP寄存器存放當前線(xiàn)程的棧頂指針,EBP寄存器中保存當前線(xiàn)程的棧底指針。CPU的EIP寄存器存放下一個(gè)CPU指令存放的內存地址,當CPU執行完當前的指令后,從EIP寄存器中讀取下一條指令的內存地址,然后繼續執行。 


參考:《Windows下的HEAP溢出及其利用》by: isno 
《windows核心編程》by: Jeffrey Richter 



摘要: 討論常見(jiàn)的堆性能問(wèn)題以及如何防范它們。(共 9 頁(yè))

前言
您是否是動(dòng)態(tài)分配的 C/C++ 對象忠實(shí)且幸運的用戶(hù)?您是否在模塊間的往返通信中頻繁地使用了“自動(dòng)化”?您的程序是否因堆分配而運行起來(lái)很慢?不僅僅您遇到這樣的問(wèn)題。幾乎所有項目遲早都會(huì )遇到堆問(wèn)題。大家都想說(shuō),“我的代碼真正好,只是堆太慢”。那只是部分正確。更深入理解堆及其用法、以及會(huì )發(fā)生什么問(wèn)題,是很有用的。

什么是堆?
(如果您已經(jīng)知道什么是堆,可以跳到“什么是常見(jiàn)的堆性能問(wèn)題?”部分)

在程序中,使用堆來(lái)動(dòng)態(tài)分配和釋放對象。在下列情況下,調用堆操作: 

事先不知道程序所需對象的數量和大小。


對象太大而不適合堆棧分配程序。
堆使用了在運行時(shí)分配給代碼和堆棧的內存之外的部分內存。下圖給出了堆分配程序的不同層。


此主題相關(guān)圖片如下:

GlobalAlloc/GlobalFree:Microsoft Win32 堆調用,這些調用直接與每個(gè)進(jìn)程的默認堆進(jìn)行對話(huà)。

LocalAlloc/LocalFree:Win32 堆調用(為了與 Microsoft Windows NT 兼容),這些調用直接與每個(gè)進(jìn)程的默認堆進(jìn)行對話(huà)。

COM 的 IMalloc 分配程序(或 CoTaskMemAlloc / CoTaskMemFree):函數使用每個(gè)進(jìn)程的默認堆。自動(dòng)化程序使用“組件對象模型 (COM)”的分配程序,而申請的程序使用每個(gè)進(jìn)程堆。

C/C++ 運行時(shí) (CRT) 分配程序:提供了 malloc() 和 free() 以及 new 和 delete 操作符。如 Microsoft Visual Basic 和 Java 等語(yǔ)言也提供了新的操作符并使用垃圾收集來(lái)代替堆。CRT 創(chuàng )建自己的私有堆,駐留在 Win32 堆的頂部。

Windows NT 中,Win32 堆是 Windows NT 運行時(shí)分配程序周?chē)谋?。所?nbsp;API 轉發(fā)它們的請求給 NTDLL。

Windows NT 運行時(shí)分配程序提供 Windows NT 內的核心堆分配程序。它由具有 128 個(gè)大小從 8 到 1,024 字節的空閑列表的前端分配程序組成。后端分配程序使用虛擬內存來(lái)保留和提交頁(yè)。

在圖表的底部是“虛擬內存分配程序”,操作系統使用它來(lái)保留和提交頁(yè)。所有分配程序使用虛擬內存進(jìn)行數據的存取。

分配和釋放塊不就那么簡(jiǎn)單嗎?為何花費這么長(cháng)時(shí)間?

堆實(shí)現的注意事項
傳統上,操作系統和運行時(shí)庫是與堆的實(shí)現共存的。在一個(gè)進(jìn)程的開(kāi)始,操作系統創(chuàng )建一個(gè)默認堆,叫做“進(jìn)程堆”。如果沒(méi)有其他堆可使用,則塊的分配使用“進(jìn)程堆”。語(yǔ)言運行時(shí)也能在進(jìn)程內創(chuàng )建單獨的堆。(例如,C 運行時(shí)創(chuàng )建它自己的堆。)除這些專(zhuān)用的堆外,應用程序或許多已載入的動(dòng)態(tài)鏈接庫 (DLL) 之一可以創(chuàng )建和使用單獨的堆。Win32 提供一整套 API 來(lái)創(chuàng )建和使用私有堆。有關(guān)堆函數(英文)的詳盡指導,請參見(jiàn) MSDN。

當應用程序或 DLL 創(chuàng )建私有堆時(shí),這些堆存在于進(jìn)程空間,并且在進(jìn)程內是可訪(fǎng)問(wèn)的。從給定堆分配的數據將在同一個(gè)堆上釋放。(不能從一個(gè)堆分配而在另一個(gè)堆釋放。)

在所有虛擬內存系統中,堆駐留在操作系統的“虛擬內存管理器”的頂部。語(yǔ)言運行時(shí)堆也駐留在虛擬內存頂部。某些情況下,這些堆是操作系統堆中的層,而語(yǔ)言運行時(shí)堆則通過(guò)大塊的分配來(lái)執行自己的內存管理。不使用操作系統堆,而使用虛擬內存函數更利于堆的分配和塊的使用。

典型的堆實(shí)現由前、后端分配程序組成。前端分配程序維持固定大小塊的空閑列表。對于一次分配調用,堆嘗試從前端列表找到一個(gè)自由塊。如果失敗,堆被迫從后端(保留和提交虛擬內存)分配一個(gè)大塊來(lái)滿(mǎn)足請求。通用的實(shí)現有每塊分配的開(kāi)銷(xiāo),這將耗費執行周期,也減少了可使用的存儲空間。

Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理內存” (搜索文章編號), 包含了有關(guān)這些主題的更多背景知識。另外,有關(guān)堆實(shí)現和設計的詳細討論也可在下列著(zhù)作中找到:“Dynamic Storage Allocation: A Survey and Critical Review”,作者 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles;“International Workshop on Memory Management”, 作者 Kinross, Scotland, UK, 1995 年 9 月(

http://www.cs.utexas.edu/users/oops/papers.html)(英文)。

Windows NT 的實(shí)現(Windows NT 版本 4.0 和更新版本) 使用了 127 個(gè)大小從 8 到 1,024 字節的 8 字節對齊塊空閑列表和一個(gè)“大塊”列表。“大塊”列表(空閑列表[0]) 保存大于 1,024 字節的塊??臻e列表容納了用雙向鏈表鏈接在一起的對象。默認情況下,“進(jìn)程堆”執行收集操作。(收集是將相鄰空閑塊合并成一個(gè)大塊的操作。)收集耗費了額外的周期,但減少了堆塊的內部碎片。

單一全局鎖保護堆,防止多線(xiàn)程式的使用。(請參見(jiàn)“Server Performance and Scalability Killers”中的第一個(gè)注意事項, George Reilly 所著(zhù),在 “MSDN Online Web Workshop”上(站點(diǎn):

http://msdn.microsoft.com/workshop/server/iis/tencom.asp(英文)。)單一全局鎖本質(zhì)上是用來(lái)保護堆數據結構,防止跨多線(xiàn)程的隨機存取。若堆操作太頻繁,單一全局鎖會(huì )對性能有不利的影響。

什么是常見(jiàn)的堆性能問(wèn)題?
以下是您使用堆時(shí)會(huì )遇到的最常見(jiàn)問(wèn)題: 

分配操作造成的速度減慢。光分配就耗費很長(cháng)時(shí)間。最可能導致運行速度減慢原因是空閑列表沒(méi)有塊,所以運行時(shí)分配程序代碼會(huì )耗費周期尋找較大的空閑塊,或從后端分配程序分配新塊。


釋放操作造成的速度減慢。釋放操作耗費較多周期,主要是啟用了收集操作。收集期間,每個(gè)釋放操作“查找”它的相鄰塊,取出它們并構造成較大塊,然后再把此較大塊插入空閑列表。在查找期間,內存可能會(huì )隨機碰到,從而導致高速緩存不能命中,性能降低。


堆競爭造成的速度減慢。當兩個(gè)或多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)數據,而且一個(gè)線(xiàn)程繼續進(jìn)行之前必須等待另一個(gè)線(xiàn)程完成時(shí)就發(fā)生競爭。競爭總是導致麻煩;這也是目前多處理器系統遇到的最大問(wèn)題。當大量使用內存塊的應用程序或 DLL 以多線(xiàn)程方式運行(或運行于多處理器系統上)時(shí)將導致速度減慢。單一鎖定的使用—常用的解決方案—意味著(zhù)使用堆的所有操作是序列化的。當等待鎖定時(shí)序列化會(huì )引起線(xiàn)程切換上下文??梢韵胂蠼徊媛房陂W爍的紅燈處走走停停導致的速度減慢。 
競爭通常會(huì )導致線(xiàn)程和進(jìn)程的上下文切換。上下文切換的開(kāi)銷(xiāo)是很大的,但開(kāi)銷(xiāo)更大的是數據從處理器高速緩存中丟失,以及后來(lái)線(xiàn)程復活時(shí)的數據重建。

堆破壞造成的速度減慢。造成堆破壞的原因是應用程序對堆塊的不正確使用。通常情形包括釋放已釋放的堆塊或使用已釋放的堆塊,以及塊的越界重寫(xiě)等明顯問(wèn)題。(破壞不在本文討論范圍之內。有關(guān)內存重寫(xiě)和泄漏等其他細節,請參見(jiàn) Microsoft Visual C++(R) 調試文檔 。)


頻繁的分配和重分配造成的速度減慢。這是使用腳本語(yǔ)言時(shí)非常普遍的現象。如字符串被反復分配,隨重分配增長(cháng)和釋放。不要這樣做,如果可能,盡量分配大字符串和使用緩沖區。另一種方法就是盡量少用連接操作。
競爭是在分配和釋放操作中導致速度減慢的問(wèn)題。理想情況下,希望使用沒(méi)有競爭和快速分配/釋放的堆??上?,現在還沒(méi)有這樣的通用堆,也許將來(lái)會(huì )有。

在所有的服務(wù)器系統中(如 IIS、MSProxy、DatabaseStacks、網(wǎng)絡(luò )服務(wù)器、 Exchange 和其他), 堆鎖定實(shí)在是個(gè)大瓶頸。處理器數越多,競爭就越會(huì )惡化。

盡量減少堆的使用
現在您明白使用堆時(shí)存在的問(wèn)題了,難道您不想擁有能解決這些問(wèn)題的超級魔棒嗎?我可希望有。但沒(méi)有魔法能使堆運行加快—因此不要期望在產(chǎn)品出貨之前的最后一星期能夠大為改觀(guān)。如果提前規劃堆策略,情況將會(huì )大大好轉。調整使用堆的方法,減少對堆的操作是提高性能的良方。

如何減少使用堆操作?通過(guò)利用數據結構內的位置可減少堆操作的次數。請考慮下列實(shí)例:

struct ObjectA {
   // objectA 的數據 
}

struct ObjectB {
   // objectB 的數據 
}

// 同時(shí)使用 objectA 和 objectB

//
// 使用指針 
//
struct ObjectB {
   struct ObjectA * pObjA;
   // objectB 的數據 
}

//
// 使用嵌入
//
struct ObjectB {
   struct ObjectA pObjA;
   // objectB 的數據 
}

//
// 集合 – 在另一對象內使用 objectA 和 objectB
//

struct ObjectX {
   struct ObjectA  objA;
   struct ObjectB  objB;
}

避免使用指針關(guān)聯(lián)兩個(gè)數據結構。如果使用指針關(guān)聯(lián)兩個(gè)數據結構,前面實(shí)例中的對象 A 和 B 將被分別分配和釋放。這會(huì )增加額外開(kāi)銷(xiāo)—我們要避免這種做法。


把帶指針的子對象嵌入父對象。當對象中有指針時(shí),則意味著(zhù)對象中有動(dòng)態(tài)元素(百分之八十)和沒(méi)有引用的新位置。嵌入增加了位置從而減少了進(jìn)一步分配/釋放的需求。這將提高應用程序的性能。


合并小對象形成大對象(聚合)。聚合減少分配和釋放的塊的數量。如果有幾個(gè)開(kāi)發(fā)者,各自開(kāi)發(fā)設計的不同部分,則最終會(huì )有許多小對象需要合并。集成的挑戰就是要找到正確的聚合邊界。


內聯(lián)緩沖區能夠滿(mǎn)足百分之八十的需要(aka 80-20 規則)。個(gè)別情況下,需要內存緩沖區來(lái)保存字符串/二進(jìn)制數據,但事先不知道總字節數。估計并內聯(lián)一個(gè)大小能滿(mǎn)足百分之八十需要的緩沖區。對剩余的百分之二十,可以分配一個(gè)新的緩沖區和指向這個(gè)緩沖區的指針。這樣,就減少分配和釋放調用并增加數據的位置空間,從根本上提高代碼的性能。


在塊中分配對象(塊化)。塊化是以組的方式一次分配多個(gè)對象的方法。如果對列表的項連續跟蹤,例如對一個(gè) {名稱(chēng),值} 對的列表,有兩種選擇:選擇一是為每一個(gè)“名稱(chēng)-值”對分配一個(gè)節點(diǎn);選擇二是分配一個(gè)能容納(如五個(gè))“名稱(chēng)-值”對的結構。例如,一般情況下,如果存儲四對,就可減少節點(diǎn)的數量,如果需要額外的空間數量,則使用附加的鏈表指針。 
塊化是友好的處理器高速緩存,特別是對于 L1-高速緩存,因為它提供了增加的位置 —不用說(shuō)對于塊分配,很多數據塊會(huì )在同一個(gè)虛擬頁(yè)中。

正確使用 _amblksiz。C 運行時(shí) (CRT) 有它的自定義前端分配程序,該分配程序從后端(Win32 堆)分配大小為 _amblksiz 的塊。將 _amblksiz 設置為較高的值能潛在地減少對后端的調用次數。這只對廣泛使用 CRT 的程序適用。
使用上述技術(shù)將獲得的好處會(huì )因對象類(lèi)型、大小及工作量而有所不同。但總能在性能和可升縮性方面有所收獲。另一方面,代碼會(huì )有點(diǎn)特殊,但如果經(jīng)過(guò)深思熟慮,代碼還是很容易管理的。

其他提高性能的技術(shù)
下面是一些提高速度的技術(shù): 

使用 Windows NT5 堆 
由于幾個(gè)同事的努力和辛勤工作,1998 年初 Microsoft Windows(R) 2000 中有了幾個(gè)重大改進(jìn):

改進(jìn)了堆代碼內的鎖定。堆代碼對每堆一個(gè)鎖。全局鎖保護堆數據結構,防止多線(xiàn)程式的使用。但不幸的是,在高通信量的情況下,堆仍受困于全局鎖,導致高競爭和低性能。Windows 2000 中,鎖內代碼的臨界區將競爭的可能性減到最小,從而提高了可伸縮性。


使用 “Lookaside”列表。堆數據結構對塊的所有空閑項使用了大小在 8 到 1,024 字節(以 8-字節遞增)的快速高速緩存??焖俑咚倬彺孀畛醣Wo在全局鎖內?,F在,使用 lookaside 列表來(lái)訪(fǎng)問(wèn)這些快速高速緩存空閑列表。這些列表不要求鎖定,而是使用 64 位的互鎖操作,因此提高了性能。


內部數據結構算法也得到改進(jìn)。
這些改進(jìn)避免了對分配高速緩存的需求,但不排除其他的優(yōu)化。使用 Windows NT5 堆評估您的代碼;它對小于 1,024 字節 (1 KB) 的塊(來(lái)自前端分配程序的塊)是最佳的。GlobalAlloc() 和 LocalAlloc() 建立在同一堆上,是存取每個(gè)進(jìn)程堆的通用機制。如果希望獲得高的局部性能,則使用 Heap(R) API 來(lái)存取每個(gè)進(jìn)程堆,或為分配操作創(chuàng )建自己的堆。如果需要對大塊操作,也可以直接使用 VirtualAlloc() / VirtualFree() 操作。

上述改進(jìn)已在 Windows 2000 beta 2 和 Windows NT 4.0 SP4 中使用。改進(jìn)后,堆鎖的競爭率顯著(zhù)降低。這使所有 Win32 堆的直接用戶(hù)受益。CRT 堆建立于 Win32 堆的頂部,但它使用自己的小塊堆,因而不能從 Windows NT 改進(jìn)中受益。(Visual C++ 版本 6.0 也有改進(jìn)的堆分配程序。)

使用分配高速緩存 
分配高速緩存允許高速緩存分配的塊,以便將來(lái)重用。這能夠減少對進(jìn)程堆(或全局堆)的分配/釋放調用的次數,也允許最大限度的重用曾經(jīng)分配的塊。另外,分配高速緩存允許收集統計信息,以便較好地理解對象在較高層次上的使用。

典型地,自定義堆分配程序在進(jìn)程堆的頂部實(shí)現。自定義堆分配程序與系統堆的行為很相似。主要的差別是它在進(jìn)程堆的頂部為分配的對象提供高速緩存。高速緩存設計成一套固定大?。ㄈ?nbsp;32 字節、64 字節、128 字節等)。這一個(gè)很好的策略,但這種自定義堆分配程序丟失與分配和釋放的對象相關(guān)的“語(yǔ)義信息”。 

與自定義堆分配程序相反,“分配高速緩存”作為每類(lèi)分配高速緩存來(lái)實(shí)現。除能夠提供自定義堆分配程序的所有好處之外,它們還能夠保留大量語(yǔ)義信息。每個(gè)分配高速緩存處理程序與一個(gè)目標二進(jìn)制對象關(guān)聯(lián)。它能夠使用一套參數進(jìn)行初始化,這些參數表示并發(fā)級別、對象大小和保持在空閑列表中的元素的數量等。分配高速緩存處理程序對象維持自己的私有空閑實(shí)體池(不超過(guò)指定的閥值)并使用私有保護鎖。合在一起,分配高速緩存和私有鎖減少了與主系統堆的通信量,因而提供了增加的并發(fā)、最大限度的重用和較高的可伸縮性。

需要使用清理程序來(lái)定期檢查所有分配高速緩存處理程序的活動(dòng)情況并回收未用的資源。如果發(fā)現沒(méi)有活動(dòng),將釋放分配對象的池,從而提高性能。

可以審核每個(gè)分配/釋放活動(dòng)。第一級信息包括對象、分配和釋放調用的總數。通過(guò)查看它們的統計信息可以得出各個(gè)對象之間的語(yǔ)義關(guān)系。利用以上介紹的許多技術(shù)之一,這種關(guān)系可以用來(lái)減少內存分配。

分配高速緩存也起到了調試助手的作用,幫助您跟蹤沒(méi)有完全清除的對象數量。通過(guò)查看動(dòng)態(tài)堆棧返回蹤跡和除沒(méi)有清除的對象之外的簽名,甚至能夠找到確切的失敗的調用者。

MP 堆 
MP 堆是對多處理器友好的分布式分配的程序包,在 Win32 SDK(Windows NT 4.0 和更新版本)中可以得到。最初由 JVert 實(shí)現,此處堆抽象建立在 Win32 堆程序包的頂部。MP 堆創(chuàng )建多個(gè) Win32 堆,并試圖將分配調用分布到不同堆,以減少在所有單一鎖上的競爭。

本程序包是好的步驟 —一種改進(jìn)的 MP-友好的自定義堆分配程序。但是,它不提供語(yǔ)義信息和缺乏統計功能。通常將 MP 堆作為 SDK 庫來(lái)使用。如果使用這個(gè) SDK 創(chuàng )建可重用組件,您將大大受益。但是,如果在每個(gè) DLL 中建立這個(gè) SDK 庫,將增加工作設置。

重新思考算法和數據結構 
要在多處理器機器上伸縮,則算法、實(shí)現、數據結構和硬件必須動(dòng)態(tài)伸縮。請看最經(jīng)常分配和釋放的數據結構。試問(wèn),“我能用不同的數據結構完成此工作嗎?”例如,如果在應用程序初始化時(shí)加載了只讀項的列表,這個(gè)列表不必是線(xiàn)性鏈接的列表。如果是動(dòng)態(tài)分配的數組就非常好。動(dòng)態(tài)分配的數組將減少內存中的堆塊和碎片,從而增強性能。

減少需要的小對象的數量減少堆分配程序的負載。例如,我們在服務(wù)器的關(guān)鍵處理路徑上使用五個(gè)不同的對象,每個(gè)對象單獨分配和釋放。一起高速緩存這些對象,把堆調用從五個(gè)減少到一個(gè),顯著(zhù)減少了堆的負載,特別當每秒鐘處理 1,000 個(gè)以上的請求時(shí)。

如果大量使用“Automation”結構,請考慮從主線(xiàn)代碼中刪除“Automation BSTR”,或至少避免重復的 BSTR 操作。(BSTR 連接導致過(guò)多的重分配和分配/釋放操作。)

摘要
對所有平臺往往都存在堆實(shí)現,因此有巨大的開(kāi)銷(xiāo)。每個(gè)單獨代碼都有特定的要求,但設計能采用本文討論的基本理論來(lái)減少堆之間的相互作用。 

評價(jià)您的代碼中堆的使用。


改進(jìn)您的代碼,以使用較少的堆調用:分析關(guān)鍵路徑和固定數據結構。


在實(shí)現自定義的包裝程序之前使用量化堆調用成本的方法。


如果對性能不滿(mǎn)意,請要求 OS 組改進(jìn)堆。更多這類(lèi)請求意味著(zhù)對改進(jìn)堆的更多關(guān)注。


要求 C 運行時(shí)組針對 OS 所提供的堆制作小巧的分配包裝程序。隨著(zhù) OS 堆的改進(jìn),C 運行時(shí)堆調用的成本將減小。


操作系統(Windows NT 家族)正在不斷改進(jìn)堆。請隨時(shí)關(guān)注和利用這些改進(jìn)。
Murali Krishnan 是 Internet Information Server (IIS) 組的首席軟件設計工程師。從 1.0 版本開(kāi)始他就設計 IIS,并成功發(fā)行了 1.0 版本到 4.0 版本。Murali 組織并領(lǐng)導 IIS 性能組三年 (1995-1998), 從一開(kāi)始就影響 IIS 性能。他擁有威斯康星州 Madison 大學(xué)的 M.S.和印度 Anna 大學(xué)的 B.S.。工作之外,他喜歡閱讀、打排球和家庭烹飪。



http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835
我在學(xué)習對象的生存方式的時(shí)候見(jiàn)到一種是在堆棧(stack)之中,如下  
CObject  object;  
還有一種是在堆(heap)中  如下  
CObject*  pobject=new  CObject();  
 
請問(wèn)  
(1)這兩種方式有什么區別?  
(2)堆棧與堆有什么區別??  
 
 
---------------------------------------------------------------  
 
1)  about  stack,  system  will  allocate  memory  to  the  instance  of  object  automatically,  and  to  the  heap,  you  must  allocate  memory  to  the  instance  of  object  with  new  or  malloc  manually.  
2)  when  function  ends,  system  will  automatically  free  the  memory  area  of  stack,  but  to  the  heap,  you  must  free  the  memory  area  manually  with  free  or  delete,  else  it  will  result  in  memory  leak.  
3)棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。  
4)堆上分配的內存可以有我們自己決定,使用非常靈活。  
---------------------------------------------------------------  
 
 
堆和棧的比較  
 
     從堆和棧的功能和作用來(lái)通俗的比較,堆主要用來(lái)存放對象的,棧主要是用來(lái)執行程序的.而這種不同又主要是由于堆和棧的特點(diǎn)決定的:  
 
     在編程中,例如C/C++中,所有的方法調用都是通過(guò)棧來(lái)進(jìn)行的,所有的局部變量,形式參數都是從棧中分配內存空間的。實(shí)際上也不是什么分配,只是從棧頂向上用就行,就好像工廠(chǎng)中的傳送帶(conveyor  belt)一樣,Stack  Pointer會(huì )自動(dòng)指引你到放東西的位置,你所要做的只是把東西放下來(lái)就行.退出函數的時(shí)候,修改棧指針就可以把棧中的內容銷(xiāo)毀.這樣的模式速度最快,當然要用來(lái)運行程序了.需要注意的是,在分配的時(shí)候,比如為一個(gè)即將要調用的程序模塊分配數據區時(shí),應事先知道這個(gè)數據區的大小,也就說(shuō)是雖然分配是在程序運行時(shí)進(jìn)行的,但是分配的大小多少是確定的,不變的,而這個(gè)"大小多少"是在編譯時(shí)確定的,不是在運行時(shí).  
 
     堆是應用程序在運行的時(shí)候請求操作系統分配給自己內存,由于從操作系統管理的內存分配,所以在分配和銷(xiāo)毀時(shí)都要占用時(shí)間,因此用堆的效率非常低.但是堆的優(yōu)點(diǎn)在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長(cháng)的時(shí)間,因此,用堆保存數據時(shí)會(huì )得到更大的靈活性。事實(shí)上,面向對象的多態(tài)性,堆內存分配是必不可少的,因為多態(tài)變量所需的存儲空間只有在運行時(shí)創(chuàng )建了對象之后才能確定.在C++中,要求創(chuàng )建一個(gè)對象時(shí),只需用new命令編制相關(guān)的代碼即可。執行這些代碼時(shí),會(huì )在堆里自動(dòng)進(jìn)行數據的保存.當然,為達到這種靈活性,必然會(huì )付出一定的代價(jià):在堆里分配存儲空間時(shí)會(huì )花掉更長(cháng)的時(shí)間!這也正是導致效率低的原因,  
 

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
堆和棧的理解和區別,C語(yǔ)言堆和棧完全攻略
Windows進(jìn)程中的內存結構
匯編語(yǔ)言代碼重定位
讓您的軟件運行起來(lái):實(shí)質(zhì)問(wèn)題和摧毀攻擊
內存的靜態(tài)分配和動(dòng)態(tài)分配的區別
軟件性能的優(yōu)化
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久