2012年7月21日 星期六

heap and stack


堆疊(Stack)和堆積(Heap)

堆(heap)和堆棧(stack)

兩種中文譯法不同

 

先參考: windows的 stack、heap記憶體(內存)管理重點筆記

 http://iambigd.blogspot.tw/2009/12/windows-stackheap.html

stack: 何謂stack? 是用來放function上auto級的變數(這樣的說法較專業),所謂auto級的就是變數是宣告在function內,它的生命(life time/ extent)在function結束後就無效了! stack的大小是由 linker來決定,以bcb為例,最大可以到0x1000000,即約16MB,你可以在project option上改。由多程式人員喜歡把object放在stack上,即用下面的宣告方式
ClassT object; 這樣整個object的資料都會配在stack上,若class小還好,大則容易overflow。故一般建議用new的方式來create objcet,只留下4byte的指標在stack上。


heap: 是用來動態使用記憶體的方式,使用的自由度最高,但需要自行善後清理。通常是用malloc/free或是new/delete來處理。heap在 windows下可以分為二種,1為default heap2為dynamic heap。default heap 可以是windows dll 等api使用,也可以app自己使用。我們開發的ap是如何來使用這個default heap呢? 可透過下面的三個api來使用GlobalAlloc 或 LocalAlloc 或GetProcessHeap來使用。事實上這個heap還再細分為fixed和movable二種。一般我們都是使用fixed。而vc++的 malloc等c run time就是用這個default heap。這個default heap的大小限制為何? 這是一個很重要的題目。另一種heap稱為 dynamic heap,這個heap就全然是我們的ap自由使用的地方。它和default heap有個不同? dynamic heap 全都是自己程式用,沒有別的api使用,另外還有一個重要的地方是這個heap可以控制多緒(multithread)同步共享heap的管理。可由 HeapCreate等相關api還有VirtualXxx api來建立。bcb本身的malloc等c run time 聽說是使用這種heap,與vc++有所不同。


另可參考
http://antrash.pixnet.net/blog/post/70456505-stack-vs-heap%EF%BC%9A%E5%9F%B7%E8%A1%8C%E6%99%82%E6%9C%9F%E5%84%B2%E5%AD%98%E5%85%A9%E5%A4%A7%E8%A6%81%E8%A7%92

有較詳細的說明

可預測性外加後進先出的生存模式,令stack無疑是最佳的存放策略。由於程式語言中變數跟函式的生命週期皆為後進先出的概念,也就是越晚產生的會越先被回收或銷毀。正因如此只要是可預測性的相關資訊都是往stack存放。此外,由於stack中的資料之存活週期規律故由系統自行產生與回收其空間即可,就不勞工程師們費心啦!

天啊!程式中竟然有不可預測其存活時間的資料存在。在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++Java中利用new語法產生的就是動態配置的物件,需存放於heap中。

奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。論述到此有些讀者可能會覺得納悶:為何在寫Java都不需要注意回收空間的問題?~答案是因為Java中會採用Garbage Collection(垃圾回收)的機制自動檢查Heap中哪些資料已經沒有被使用,當確認資料已經沒有使用會自動將空間回收,如此工程師就專注撰寫程式即可,不用擔心記憶體回收不當等問題。

The conclusion is…當產生stack overflow一般是因為過多的函式呼叫(例如:遞迴太深)、或區域變數使用太多,此時請試著將stack size調大一點,另外檢查看看函式的呼叫跟變數的使用量。反之,當發生heap overflow請檢查是否都有正確將heap space的資料回收,另外採行的動態配置是否合理,不要過渡濫用而new出無謂的空間,若真的是程式過於複雜造成,請將heap size調大一些。


另一個參考點,整理得很不錯
http://alonchang.pixnet.net/blog/post/41170300-heap-%E5%92%8C-stack

二、<Heap>和<Stack>的理論知識
2.1申請方式
stack:
由系統自動分配。 例如,聲明在函數中一個局部變數 int b; 系統自動在<Stack>中為b開闢空間
heap:
需要programmer自己申請,並指明大小,在c中malloc函數
如p1 = (char *)malloc(10);
在C++中用new運算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在<Stack>中的。
2.2
申請後系統的回應
<Stack>:只要<Stack>的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示<Stack>溢出。
<Heap>:首先應該知道作業系統有一個記錄空閒記憶體位址的link-list,當系統收到程式的申請時,
會 遍曆該link-list,尋找第一個空間大於所申請空間的<Heap>結點,然後將該結點從空閒結點link-list中刪除,並將該結點 的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首位址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本記憶體空 間。另外,由於找到的<Heap>結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒link-list中。
2.3申請大小的限制
<Stack>: 在Windows下,<Stack>是向低位址擴展的資料結構,是一塊連續的記憶體的區域。這句話的意思是<Stack>頂的位 址和<Stack>的最大容量是系統預先規定好的,在 WINDOWS下,<Stack>的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超 過<Stack>的剩餘空間時,將提示overflow。因此,能從<Stack>獲得的空間較小。
<Heap>:<Heap> 是向高位址擴展的資料結構,是不連續的記憶體區域。這是由於系統是用link-list來存儲的空閒記憶體位址的,自然是不連續的,而link-list 的遍曆方向是由低位元址向高位址。<Heap>的大小受限於電腦系統中有效的虛擬記憶體。由此可見,<Heap>獲得的空間比較 靈活,也比較大。
2.4申請效率的比較:
<Stack>由系統自動分配,速度較快。但programmer是無法控制的。
<Heap>是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配記憶體,他不是在<Heap>,也不是在<Stack>是直接在process的位址空間中保留一快記憶體,雖然用起來最不方便。但是速度快,也最靈活
2.5<Heap>和<Stack>中的存儲內容
<Stack>: 在函數調用時,第一個進<Stack>的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的位址,然後是函數的各個參數,在大多 數的C編譯器中,參數是由右往左入<Stack>的,然後是函數中的局部變數。注意靜態變數是不入<Stack>的。
當本次函數調用結束後,局部變數先出<Stack>,然後是參數,最後<Stack>頂指標指向最開始存的位址,也就是主函數中的下一條指令,程式由該點繼續運行。
<Heap>:一般是在<Heap>的頭部用一個位元組存放<Heap>的大小。<Heap>中的具體內容有programmer安排。


stack view
Schematic view of the stack

2012年7月19日 星期四

synthesize and property的新方法

轉貼: http://yehnan.blogspot.tw/2012/07/objective-c.html


對於property也有很多新東西。

原本會這麼寫:

//Person.h
@interface Person : NSObject
{
  NSString *_name;

}


@property (strong) NSString *name;
@end

//Person.m
@implementation Person

@synthesize name = _name;

@end

可以把instance variable的宣告改放在@implementation裡:
//Person.h
@interface Person : NSObject
{
  NSString *_name;

}
@property (strong) NSString *name;
@end

//Person.m
@implementation Person
{
  NSString *_name;
}

@synthesize name = _name;

@end

而且,既然用了@synthesize,根本就不需要自己宣告instance variable了:
//Person.h
@interface Person : NSObject
{
  NSString *_name;

}
@property (strong) NSString *name;
@end

//Person.m
@implementation Person
{
  NSString *_name;
}


@synthesize name = _name;

@end

以上是本來就有的,現在,在Xcode 4.4(與之後的版本),連@synthesize都可以省略了:
//Person.h
@interface Person : NSObject
{
  NSString *_name;

}
@property (strong) NSString *name;
@end

//Person.m
@implementation Person
{
  NSString *_name;
}


@synthesize name = _name;

@end

也就是說,synthesize是"預設"的行為了,原本property要寫好幾行程式,現在只需一行即可,編譯器會自動加入:

@synthesize name = _name;

預設會在property名字前面加上底線「_」作為instance variable的名字。


注意,為了相容於舊版,若你寫了
@synthesize name;
則等同於
@synthesize name = name;