堆、棧、BSS、Data、code區、靜態存儲區、文字常量區

 在計算機領域,堆棧是一個不容忽視的概念,但是很多人甚至是計算機專業的人也沒有明確堆棧其實是兩種數據結構。

要點:
堆:順序隨意
棧:先進後出
堆和棧的區別
一、預備知識—程序的內存分配
一個由c/C++編譯的程序佔用的內存分爲以下幾個部分
1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放(就會造成內存泄漏的問題),程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。
3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域(data), 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域(BSS,Block Started by Symbol)。 - 程序結束後有系統釋放(在整個程序的執行過程中都是有效的)
4、文字常量區 —常量字符串就是放在這裏的。 程序結束後由系統釋放 (文字常量區內的數據可以修改嗎?)
5、程序代碼區(code)—存放函數體的二進制代碼。
二、例子程序
這是一個前輩寫的,非常詳細
//main.cpp
int a = 0; 全局初始化區
char *p1; 全局未初始化區
main()
{
int b; 棧
char s[] = "abc"; 棧                  //abc是在棧裏面,而下面的123456/0卻在在常量區內,要注意這兩種情況的區別
char *p2; 棧
char *p3 = "123456"; 123456/0在常量區,p3在棧上。
static int c =0; 全局(靜態)初始化區
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20字節的區域就在堆區。
strcpy(p1, "123456"); 123456/0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。 (所謂的優化是什麼意思,是指P1和P2指向的是同一塊內存嗎?)
}

二、堆和棧的理論知識

2.1申請方式
stack:
由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中爲b開闢空間
heap:
需要程序員自己申請,並指明大小,在c中malloc函數

如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在棧中的。

2.2 申請後系統的響應
棧:只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出。
堆:首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,
會 遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內 存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大 小,系統會自動的將多餘的那部分重新放入空閒鏈表中。

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

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

2.5堆和棧中的存儲內容
棧: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器中,參數是由 右往左入棧的,然後是函數中的局部變量。注意靜態變量是不入棧的。 當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。

堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

2.6存取效率的比較
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運行時刻賦值的;
而bbbbbbbbbbb是在編譯時就確定的;
但是,在以後的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
比如:
#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
第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。

2.7小結:

堆和棧的區別可以用如下的比喻來看出:
使用棧就象我們去飯館裏吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗
菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動手做喜歡吃的菜餚,比較麻煩,但是比較符合自己的口味,而且自由度大。
堆和棧的區別主要分:

操作系統方面的堆和棧,如上面說的那些,不多說了。

還有就是數據結構方面的堆和棧,這些都是不同的概念。這裏的堆實際上指的就是(滿足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或數據結構。

雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大區別的,連着叫只是由於歷史的原因。

摘要: 討論常見的堆性能問題以及如何防範它們。
前言
您 是否是動態分配的 C/C++ 對象忠實且幸運的用 戶?您是否在模塊間的往返通信中頻繁地使用了“自動化”?您的程序是否因堆分配而運行起來很慢?不僅僅您遇到這樣的問題。幾乎所有項目遲早都會遇到堆問 題。大家都想說,“我的代碼真正好,只是堆太慢”。那只是部分正確。更深入理解堆及其用法、以及會發生什麼問題,是很有用的。

什麼是堆?
(如果您已經知道什麼是堆,可以跳到“什麼是常見的堆性能問題?”部分)

在程序中,使用堆來動態分配和釋放對象。在下列情況下,調用堆操作:

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


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

GlobalAlloc/GlobalFree:Microsoft Win32 堆調用,這些調用直接與每個進程的默認堆進行對話。

LocalAlloc/LocalFree:Win32 堆調用(爲了與 Microsoft Windows NT 兼容),這些調用直接與每個進程的默認堆進行對話。

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

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

Windows NT 中,Win32 堆是 Windows NT 運行時分配程序周圍的薄層。所有 API 轉發它們的請求給 NTDLL。

Windows NT 運行時分配程序提供 Windows NT 內的核心堆分配程序。它由具有 128 個大小從 8 到 1,024 字節的空閒列表的前端分配程序組成。後端分配程序使用虛擬內存來保留和提交頁。

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

分配和釋放塊不就那麼簡單嗎?爲何花費這麼長時間?

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

當應用程序或 DLL 創建私有堆時,這些堆存在於進程空間,並且在進程內是可訪問的。從給定堆分配的數據將在同一個堆上釋放。(不能從一個堆分配而在另一個堆釋放。)

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

典 型的堆實現由前、後端分配程序組成。前端分配程序維持固定大小塊的空閒列表。對於一次分配調用,堆嘗試從前端列表找到一個自由塊。如果失敗,堆被迫從後端 (保留和提交虛擬內存)分配一個大塊來滿足請求。通用的實現有每塊分配的開銷,這將耗費執行週期,也減少了可使用的存儲空間。

Knowledge Base 文章 Q10758,“用 calloc() 和 malloc() 管理內存” (搜索文章編號), 包含了有關這些主題的更多背景知識。另外,有關堆 實現和設計的詳細討論也可在下列著作中找到:“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 的實現(Windows NT 版本 4.0 和更新版本) 使用了 127 個大小從 8 到 1,024 字節的 8 字節對齊塊空閒列表和一個“大 塊”列表。“大塊”列表(空閒列表[0]) 保存大於 1,024 字節的塊。空閒列表容納了用雙向鏈表鏈接在一起的對象。默認情況下,“進程堆”執行收 集操作。(收集是將相鄰空閒塊合併成一個大塊的操作。)收集耗費了額外的週期,但減少了堆塊的內部碎片。

單一全局鎖保護堆,防止多線程式的使用。(請參見“Server Performance and Scalability Killers”中的第一個注意事項, George Reilly 所著,在 “MSDN Online Web Workshop”上(站點:http://msdn.microsoft.com/workshop/server/iis/tencom.asp(英文)。)單一全局鎖本質上是用來保護堆數據結構,防止跨多線程的隨機存取。若堆操作太頻繁,單一全局鎖會對性能有不利的影響。

什麼是常見的堆性能問題?
以下是您使用堆時會遇到的最常見問題:

分配操作造成的速度減慢。光分配就耗費很長時間。最可能導致運行速度減慢原因是空閒列表沒有塊,所以運行時分配程序代碼會耗費週期尋找較大的空閒塊,或從後端分配程序分配新塊。


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


堆 競爭造成的速度減慢。當兩個或多個線程同時訪問數據,而且一個線程繼續進行之前必須等待另一個線程完成時就發生競爭。競爭總是導致麻煩;這也是目前多處理 器系統遇到的最大問題。當大量使用內存塊的應用程序或 DLL 以多線程方式運行(或運行於多處理器系統上)時將導致速度減慢。單一鎖定的使用—常用的解 決方案—意味着使用堆的所有操作是序列化的。當等待鎖定時序列化會引起線程切換上下文。可以想象交叉路口閃爍的紅燈處走走停停導致的速度減慢。
競爭通常會導致線程和進程的上下文切換。上下文切換的開銷是很大的,但開銷更大的是數據從處理器高速緩存中丟失,以及後來線程復活時的數據重建。

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


頻繁的分配和重分配造成的速度減慢。這是使用腳本語言時非常普遍的現象。如字符串被反覆分配,隨重分配增長和釋放。不要這樣做,如果可能,儘量分配大字符串和使用緩衝區。另一種方法就是儘量少用連接操作。
競爭是在分配和釋放操作中導致速度減慢的問題。理想情況下,希望使用沒有競爭和快速分配/釋放的堆。可惜,現在還沒有這樣的通用堆,也許將來會有。

在所有的服務器系統中(如 IIS、MSProxy、DatabaseStacks、網絡服務器、 Exchange 和其他), 堆鎖定實在是個大瓶頸。處理器數越多,競爭就越會惡化。

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

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

struct ObjectA {
    // objectA 的數據
}

struct ObjectB {
    // objectB 的數據
}

// 同時使用 objectA 和 objectB

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

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

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

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

避免使用指針關聯兩個數據結構。如果使用指針關聯兩個數據結構,前面實例中的對象 A 和 B 將被分別分配和釋放。這會增加額外開銷—我們要避免這種做法。


把帶指針的子對象嵌入父對象。當對象中有指針時,則意味着對象中有動態元素(百分之八十)和沒有引用的新位置。嵌入增加了位置從而減少了進一步分配/釋放的需求。這將提高應用程序的性能。


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


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


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

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

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

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

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


使 用 “Lookaside”列表。堆數據結構對塊的所有空閒項使用了大小在 8 到 1,024 字節(以 8-字節遞增)的快速高速緩存。快速高速緩存 最初保護在全局鎖內。現在,使用 lookaside 列表來訪問這些快速高速緩存空閒列表。這些列表不要求鎖定,而是使用 64 位的互鎖操作,因此提 高了性能。


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

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

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

典 型地,自定義堆分配程序在進程堆的頂部實現。自定義堆分配程序與系統堆的行爲很相似。主要的差別是它在進程堆的頂部爲分配的對象提供高速緩存。高速緩存設 計成一套固定大小(如 32 字節、64 字節、128 字節等)。這一個很好的策略,但這種自定義堆分配程序丟失與分配和釋放的對象相關的“語義信 息”。

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

需要使用清理程序來定期檢查所有分配高速緩存處理程序的活動情況並回收未用的資源。如果發現沒有活動,將釋放分配對象的池,從而提高性能。

可以審覈每個分配/釋放活動。第一級信息包括對象、分配和釋放調用的總數。通過查看它們的統計信息可以得出各個對象之間的語義關係。利用以上介紹的許多技術之一,這種關係可以用來減少內存分配。

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

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

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

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

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

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

摘要
對所有平臺往往都存在堆實現,因此有巨大的開銷。每個單獨代碼都有特定的要求,但設計能採用本文討論的基本理論來減少堆之間的相互作用。

評價您的代碼中堆的使用。


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


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


如果對性能不滿意,請要求 OS 組改進堆。更多這類請求意味着對改進堆的更多關注。


要求 C 運行時組針對 OS 所提供的堆製作小巧的分配包裝程序。隨着 OS 堆的改進,C 運行時堆調用的成本將減小。


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



http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835
我在學習對象的生存方式的時候見到一種是在堆棧(stack)之中,如下  
CObject   object;  
還有一種是在堆(heap)中   如下  
CObject*   pobject=new   CObject();  

請問  
(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)堆上分配的內存可以有我們自己決定,使用非常靈活。  
---------------------------------------------------------------  


堆和棧的比較  

      從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:  

      在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變量,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂 向上用就行,就好像工廠中的傳送帶(conveyor   belt)一樣,Stack   Pointer會自動指引你到放東西的位置,你所要做的只是把東 西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷燬.這樣的模式速度最快,當然要用來運行程序了.需要注意的是,在分配的時候,比如爲一個 即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個" 大小多少"是在編譯時確定的,不是在運行時.  

      堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理 的內存分配,所以在分配和銷燬時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆裏分配多少存儲空間,也不必知道存儲的數據 要在堆裏停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因爲多態變量所需的存儲空間只有 在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用new命令編制相關的代碼即可。執行這些代碼時,會在堆裏自動進行數據的保存. 當然,爲達到這種靈活性,必然會付出一定的代價:在堆裏分配存儲空間時會花掉更長的時間!這也正是導致效率
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章