星期五, 12月 25, 2009

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

1/ windows的flat 模式: 這題目要講起來可以很深,包含許多的組合語言的東西。但在此我只想說一個結果,就是因為windows是使用這樣的一個記憶體管理位址模式,所以使得每個process都可以有私有的4G的「定址」能力,但因為windows當初要相容MIPS R4000 architecture,所以高位址的2G部份放的是kernel code,而我們的ap只能用低部份的2G,所以正確的來講,我們的程式能夠使用2G的定址能力。

2/虛擬記憶體: 在80386處理器後,cup support 保護模式,paging 模式。我們都知道windows會使用硬碟來充當ram使用,當ram不足的時候。這就是用paging的cpu功能。有了這個功能,os就可以讓每個process都可以使用2G的data,即使ram只有幾MB。在標題1我們有提到「定址」的這個詞,這只是說cpu的register(暫存器)有能力讓我們定出那麼大的空間。但是可以定出那麼大麼大的空間並不表示你一定先要有那麼大的ram在電腦上。定址是一回事,配置(allocate)又是另一回事,但二者都很重要。比如在dos 80286前的時代,有所謂的640k的限制(那是指定址能力先天不足),所以你就算是買了4MB的ram插在電腦上,還是浪費。回到主題,因為386以後有paging的能力,加上register是32位元,所以就組合出一種相當完美的記憶體管理方法: 每個process都有虛擬的4G記憶體,且可以真的使用到4G,即使你的ram沒有4G,windows會把你的虛擬記憶體資料放到硬碟上。為何說是「虛擬」?就是指你不一定真的有那麼多的真實的ram。比如說你真實的ram有128MB,而你的程式超大,用到512MB,那麼當程式在跑的時候,只會把正需要的code往128MB的ram上放,而把尚不用的放(paging)在硬碟上。

3/ 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上。又如這些天許多玩matalab的人問到2維array overflow的問題,也是因為把array宣告在stack上所造成。要解決的方式還是一樣,把它用new的方式create到heap上(稍後會講)。但二維的動態產生array需要一些技巧,可查一下我的文章。還有,使用recursive function的人也要特別注意overfolw的問題。必要的話可以改寫演算法不要用遞回。stack的使用很方便,因為不用僅管使用,無需去自行清理,唯不要overflow就很棒!

4/ 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++有所不同。

5/heap size(一): 這是一個很重要的題目。到底 heap size的限制為何? 答案是… 無論是dynamic 或是 defualt heap都是限制在定址能力上,即以windwos來說,就是2G。但是奇怪了,project option的linker選項中有提到一個heap size的地方,就像stack也在那設定一樣。而這個選項裡的最大值一樣只能到0x1000000,即16MB左右。而實際上許多人都曾在程式中要求超過百mb以上的heap,怎都沒事?? 而這個設定值有啥用? 答案是 : 所謂的min size的值是指commit的值,max size是reserve的值。commit / reserve這二個詞在windows api 與vc++中常用。我大概講一下…所謂的reserve是指程式可以「要求註冊」保留虛擬位址的大小。請注意是「虛擬位址」,比個喻,就像是先向政府預定門牌號1-100號都將是我家所用,但還沒建好房子。而commit size則是每次我建房子都是以幾個門牌為單位建立,比如說,commit=4,即表每次都以4間房子來建,直到100個門牌都用完。所有windows的記憶管理都是用reserve/commit的方式在處理。都是先要求將有多少位址會用到,然後等實值用到時再依commit的大小來一單位一單位的分配實值記憶體給你用。

6/ heap size (二) : 之前有提到像vc++ 的malloc都是用heap。最早我說它的限制是2G,但剛在上面又說linker設定上只能最大是16mb,若真的是16mb,那malloc早就應該overflow n次了,很茅盾! 事實上… heap的大小是真的限制在2G,即使用都不去改option中的值,它還是2G。哇~~那那設定是假的? 不…那設定正確講是為了效能。怎說呢? 雖然最大可使用是2G,但windows並非一開始就拿2G等著你用。它是在需要的時候先行要求位址(門牌),在一一的配給你。在option上設定就好比你打電話去餐聽先定位,先定個15個位子(reserve size),並且向服務生說我們會分批進去,而每批都是3個人為單位(commit size)。服務人員想講完電話後想…我們這個時間的生意這麼好,若事先就空著15個位子,那就少賺了…於是先空3個位子,反正每一批都是3人來。於是第一批到後,餐廳再空3人位子出來等,直到15人都到了! 這種預先定位的好處就是你進去餐廳後不必多廢話,進去就入座! 很有效能!

7/ heap size(三): 把剛的比喻故事延申下去…萬一我臨時又有6個朋友也要一起去吃飯,而我剛只定了15位,餐廳能否再開6位給我呢? 當然沒問題! 只是餐聽的人員說,你要稍等一下,等我再補登記新人數後再配位子入座! 所以事實上defult heap 非靜態的只到你reserve的大小就停了,它可以再自動的變大,只是需要一點時間和資源。記住! 是可以到2G沒問題的! 而從這比喻中你應該可以了解,若是開始的時候我們就正確的預定好人數,就不會多花一點時間補登記。反過來說,即使亂預定也沒關係,都還是有位子可以座,只是要多補n次的登記時間!

8/ 何時該使用default heap,何時用dynamic heap? 這問題其實不是很重要,為了portable,用malloc/ new來處理就好,compiler會選用其中一種來處理。除了在開發multithead的程式時,當你不希望主thread中的heap被二個以上的thead同時存取,你可以用dynamic heap來建立,即用HeapCreate( )的預設建立方法。當然你也可以用這個function建立出一般的fixed的heap,只要將參數1改為HEAP_NO_SERIALIZE,就可以!


9/ 補一下commit值的重要性,若每次你new的東西是很大的,而你把commit值又設很小,那麼每new一次就要補手續n次。比如說每次要new 40k 的資料,但你commit只設4k,那…new一次就配置手續10次。當然,若你說把commit設成100k,那麼一次就搞定,但…也有個小缺點 : 剩下的配給你的真實的60k浪費在那裡了。給別的process用那60k不好嗎? 所以…設成40-50k是不是比較折衷的好設定呢?!

轉貼作者: 蕭沖 qs.xiao@gmail.com
http://delphi.ktop.com.tw/board.php?cid=168&fid=912&tid=83727

沒有留言:

張貼留言

留個話吧:)