350 likes | 457 Views
使用 C/C++ 語言. 楊正宏 編著 全華科技圖書股份有限公司 印行. 第十一章 動態記憶體管理 (1/2). 11-1 前言 11-2 記憶體分配方法 11-3 邊界標識法 (Boundary Tag Method) 11-3-1 可利用空間串列的結構 11-3-2 分配演算法 11-3-3 回收演算法 11-4 夥伴系統 (Buddy System) 11-4-1 可利用空間串列的結構 11-4-2 分配演算法 11-4-3 回收演算法. 第十一章 動態記憶體管理 (2/2).
E N D
使用C/C++語言 楊正宏 編著 全華科技圖書股份有限公司 印行
第十一章 動態記憶體管理(1/2) • 11-1 前言 • 11-2 記憶體分配方法 • 11-3 邊界標識法(Boundary Tag Method) • 11-3-1 可利用空間串列的結構 • 11-3-2 分配演算法 • 11-3-3 回收演算法 • 11-4夥伴系統(Buddy System) • 11-4-1 可利用空間串列的結構 • 11-4-2 分配演算法 • 11-4-3 回收演算法
第十一章 動態記憶體管理(2/2) • 11-5 費氏夥伴系統(Fibonacci Buddy System) • 11-6 廢置單元收集 • 11-7 廢置單元收集的改良 • 11-8 記憶體壓縮
前言(1/2) • 透過作業系統取得所需記憶體(memory),並在執行完成後將所佔記憶體歸還作業系統,此項管理工作便稱為「動態記憶體管理」。 • 動態記憶體管理的基本工作是系統如何因應使用者提出的"記憶體分配需求",以及如何"回收(釋放)"記憶體,以便當新的"請求"產生時,重新進行分配。
U1 U2 U3 U4 U5 U6 U1 U3 U4 U6 位址低 位址低 位址高 位址高 前言(2/2) • 系統執行初期 • 系統執行若干時間之後
記憶體分配方法(1/2) • 常用的三種方法包括: • 最先合適法(First-Fit) • 最佳合適法(Best-Fit) • 最差合適法(Worst-Fit) • 根據可利用閒置串列的多寡可分: • 單一區段 • 多區段
記憶體分配方法(2/2) • 最先合適法(First-Fit): • 自可供利用空間區段中,找到第一塊大於n的閒置區段,將其中一部份分配給使用者;回收時,只要將釋放的閒置區段插入在鏈結串列的開頭即可。 • 最佳合適法(Best-Fit): • 自可供利用空間區段中,找出一塊大小最接近n的閒置區段分配給使用者。為避免搜尋浪費的時間,先將區段自小到大排序。分配時,只需找到第一個大於n的閒置區段即可分配;回收時,必須將釋放的閒置區段插入到合適的位置上去。 • 最差合適法(Worst-Fit): • 自可供利用閒置區段中,找出最大的閒置區段分配給使用者。將閒置區段由大到小排序。每次分配時,只需將第一個區段的其中一部份分配給使用者即可;回收時,亦需將釋放的閒置區段插入到適當位置上去。
邊界標識法(Boundary Tag Method) (1/8) • 是一種作業系統中用以進行動態分區分配的記憶體管理方法 • 配合最先合適法來管理記憶體,可減少搜尋時間。 • 特點: • 在於每個記憶體區段的開頭部份和尾部兩個邊界上分別設有標籤,以標識該區域為占用區段或閒置區段,使得在回收使用者釋放的閒置區段時,易於判別物理位置上與其相鄰的記憶體區域是否為閒置區段,以便將所有位址中連續的閒置記憶體區組合成一個大的閒置區段。
1000 3100 5900 0 1500 0 0800 0 4100 pav 0 0 0 pav 0 100K 0 pav 1000 3100 5900 0 1500 0 0800 0 4100 0 0 邊界標識法(2/8) 可利用空間串列的結構 • 起始狀態 • 執行若干時間後 • 進行再分配後的狀態
邊界標識法(3/8) • 分配演算法規定: • 假設所找到的閒置區段的容量為m個字組(包括頭部和底部),而每次分配只是從中分配n個字組給使用者,剩餘m-n個字組大小的節點仍留在鏈結串列中,則在若干次分配之後,鏈給串列中會出現一些容量極小而分配不出去的閒置區段,這就大大減慢了分配(搜尋)的速度。 • 彌補的辦法是:選定一個適當的常量e,當m-n≦e時,將容量為m的閒置區段整塊分配給使用者;反之,只分配其中n個字的記憶體。同時,為了避免修改指標,約定將該節點中位址較大部份分配給使用者。
邊界標識法(4/8) • 分配演算法規定: • 如果每次分配都從同一個節點開始搜尋的話,勢必造成記憶體小的節點集中在開頭指標 pav所指節點附近,反而增加查詢較大閒置區段的時間。反之,如果每次分配從不同的節點開始進行搜尋,使分配後剩餘的小區段均勻地分布在鏈結串列中,則可避免上述弊病。 • 可行的方法是每次分配之後,令指標pav指向剛進行過分配的節點的後繼節點。
邊界標識法(5/8) • 回收演算法 • 釋放區段的左、右鄰區均為佔用區段的情況 • 只要作簡單插入即可。由於在按最先合適法進行分配時邊界標識法對可利用空間串列的結構並沒有任何要求,則新的閒置區段插入在串列中任何位置均可。 • 簡單的做法就是插入在pav指標所指節點之前(或之後)。
20,000 0 30,000 左鄰區 釋放區段 0 邊界標識法(6/8) • 回收演算法 • 釋放區段約左鄰區為閒置區段,而右鄰區為佔用區段的情況 • 由於釋放區段的開頭部份和左鄰閒置區段的尾部毗鄰,因此只要改變左鄰閒置區段的節點,增加節點size欄的值,且更新設置節點的尾部即可。
0 35,000 釋放區段 0 15,000 右鄰區 右鄰區 0 邊界標識法(7/8) • 回收演算法 • 釋放區段的右鄰區為閒置區段,左鄰區為佔用區段 • 由於釋放區段的尾部和右鄰閒置區段的開頭部份毗鄰,當串列中節點由原來右約有鄰閒置區段變成合併後的大閒置區段時,區段的尾部指標位置不變,但開頭的指標位置要移動,因此,鏈結串列中的指標也必須要移動。
0 45,000 0 15,000 左鄰區 右鄰區 釋放區段 右鄰區 0 邊界標識法(8/8) • 回收演算法 • 釋放區段的左、右鄰區均為閒置區段 • 為使三個閒置區段連接在一起成為一個大閒置區段留在可利用空間串列中,只要增加左鄰閒置區段的space容量,同時在鏈結串列中刪去右鄰閒置區段節點即可。
夥伴系統(Buddy System) (1/7) • 一種動態記憶體之管理方法。和邊界標識法類似 • 使用者提出申請時,分配一塊大小“適當”的記憶體給使用者;反之,在使用者釋放記憶體時即回收。 • 無論是佔用區段或閒置區段,其大小均為2的次方值。 • 當使用者申請n大小的記憶體時,就分配區段大小為2k個字組(byte) 給它,其中2k-1≦n≦2k。 • 在可利用空間串列中的閒置區段大小只能是2的次方值。若可利用記憶體容量為2m,則閒置區段的大小只可能為20,21,‥,2m。
20 ^ 21 ^ 2k ^ llink tag=0 kval rlink header space 2m 0 m 空間區段的節點結構 Nodesize first 串列的初始狀態 夥伴系統(2/7) • 可利用空間串列的結構
20 ^ 2k-1 ^ 2k 0 k 0 k 0 k 2m ^ 分配前的串列 夥伴系統(3/7) • 分配演算法 節點1 節點2 節點3
20 ^ 0 k-1 用剩餘的另一半節點 2k-1 2k 0 k 0 k 節點2 節點3 2m ^ 夥伴系統(4/7) • 分配演算法
2k 夥伴系統(5/7) • 假設從2k子串列中刪除的節點的起始位址為p,且假設分配給使用者的佔用區段的初始位址也為p,則插入上述子串列的新節點的起始位址分別為p+2k-i,p+2k-i+1,…,p+2k-1 如圖所示
夥伴系統(6/7) • 回收演算法 • 在使用者釋放佔用區段時,系統需將此閒置區段插入到可利用閒置區段中去。 • 在夥伴系統中同樣也有一個位址相鄰的閒置區段合併成大區段的問題,但是在夥伴系統中僅考慮"互為夥伴"的合併。 0 空閒區段A 空閒區段B A1 A2 A3
夥伴系統(7/7) • 優點: • 演算法較簡單、速度快。 • 缺點: • 由於只合併伙伴,容易產生碎片。
21 8 13 3 5 5 8 2 3 費氏夥伴系統(Fibonacci Buddy System)(1/3) • 費氏的分配系統
費氏夥伴系統(2/3) • 重點不在分配記憶體的區塊,而是兩個具有共同父節的相鄰區塊,如何合併成較大的區塊。 • 為了記憶體釋回工作操作順利,設定counter變數,以記錄區塊的分裂關係,步驟: • 使用區塊中最大者,即節點counter = 0 • 當區塊分裂為二時,以遞迴方式定義counter的值: 左區塊的counter = counter+1。 右區塊的counter = 0 (歸零)。
counter = 0 21 1 0 8 13 2 0 1 0 3 5 5 8 1 0 2 3 費氏夥伴系統(3/3) • 每個區塊填上counter數字
P2 P3 1 1 P1 1 廢置單元收集(1/5) • 是使用者不再使用而沒有回收的記憶體區段。 • 特點在於使用者請求記憶體時進行分配,在使用者釋放記憶體時進行回收。
廢置單元收集(2/5) • 參考計數法(Reference Count Method):在所有子串列或一般化串列上增加一個串列開頭節點,並設立一個“計數器”,每當有一指標指向此節點則計數器加1直到計數器為0時,節點才被釋放。 • 收集廢置單元(Garbage Collection):在程式執行的過程中,對所有的鏈結串列節點,不管它是否還有用,都不回收,直到整個記憶體用完。當記憶體用完時,才暫時中斷執行程式,將之前釋放的記憶體空間回收當作使用者使用的空間,當記憶體用完時,再度執行此動作。
廢置單元收集(3/5) • 收集廢置單元的方式分兩階段進行: • 標示階段(marking phase): 對所有佔用節點加上標示,可在每個節點上再加設一個標示(mark),以區別是否為正在作用中的區段。 • 搜集階段(collection phase): 當記憶體不夠時收集標示為非在作用區段的記憶體。
廢置單元收集(4/5) • 遞迴的演算法: • 若串列為空,則不走訪;若有資料,則作標示。 • 在允許使用遞迴的高階語言中非常容易撰寫。 • 需要使用堆疊,由於串列的層次無法估計,使得堆疊的容量不易確定,除非是在記憶體中宣告一個相當大的堆疊空間,否則標示時,可能因為超過堆疊空間而使系統癱瘓。
廢置單元收集(5/5) • 非遞迴的演算法: • 類似圖形的兩種優先搜尋(DFS和BFS),可分為縱向或橫向。 • 當串列非空時,使用橫(縱)向優先搜尋走訪時,在對串列節點加標示後,先順串列開頭指標逐層向下對串列開頭加標示,同時將同(下)層非空且未加標示的串列尾端指標依次推入堆疊,直到串列開頭為空串列或為元素節點時停止,然後退出堆疊取出上一層的串列尾端指標。 • 反覆上述程序,直到堆疊空為止。
記憶體壓縮(1/4) • 做法: • 當有使用者釋放記憶體即進行回收壓縮。 • 在程式執行過程中不回收使用者隨時釋放的記憶體,直到可利用空間不夠分配或區塊指標指向最高位址時才進行記憶體壓縮。此時壓縮的目的是將區塊中所有的閒置區段連成一片,即將所有的佔用區段都集中到可利用空問的低位址區,而剩餘的高位址區成為一整個位址連續的閒置區段。
起始 位址 長度 A1 0 5 A2 5 6 A3 11 3 A4 14 5 記憶體壓縮(2/4) • 壓縮後前記憶體
記憶體壓縮(3/4) • 壓縮後的記憶體 起始 位址 長度 A1 0 5 A2 A3 5 3 A4 8 5
記憶體壓縮(4/4) • 記憶體壓縮前 記憶體壓縮後 壓縮 pointer pointer