1. 程式人生 > >連續的儲存空間分配(2.2.2)

連續的儲存空間分配(2.2.2)

看了一點演算法章節的,感覺看得非常辛苦。想跳去資料結構章節看看怎麼樣,結果對於我來說依然是晦澀難懂。資料結構第二節講的是線性結構,第一小節簡單地介紹了一下常用的線性結構,有堆疊、佇列和雙端佇列。然後開始講儲存空間分配的方法,第二小節講連續儲存空間的分配,第三小節講連結串列形式的分配。

感覺上課學的資料結構真的是弱爆了,對於連續儲存空間分配我的反應只有陣列,各種新增和刪除操作。但是書裡分析的卻是這樣一種情況,有n個堆疊(每一個都需要連續儲存空間)以及一大段連續的儲存空間,怎樣才能合理分配,並且記憶體溢位時怎樣處理。注意為了更高效的利用記憶體空間,堆疊的大小是可變的,就像一個可以動態改變大小的陣列一樣。

有一個比較簡單的方法,當某一個堆疊溢位的時候,分別向前和向後找,看是否有其他堆疊有多餘的空間,找到以後通過整體向前或向後平移資料從而騰出一個空間給溢位的堆疊。但這種方法僅僅是處理了堆疊溢位的情況並不能減少堆疊溢位的發生次數,我們可以通過合理地初始化各個堆疊的大小來減少堆疊溢位次數。比如說,若每一個堆疊預計的大小都相同,則我們可以將它們平均分配到整段記憶體中。可是無論做出怎樣合理的初始化,都僅僅可以減少有限次數的堆疊溢位,而且一般情況下只是在程式執行的早期階段比較有效。

基於這種情況,書中又提出了一種更好的方法,在堆疊溢位後不僅僅給溢位的堆疊分配多一個元素空間,而是根據所有堆疊的增長速度重新給它們分配記憶體空間,以預留更多的空間給增長速度快的堆疊。現在假設共有n個堆疊,堆疊i溢位,處理過程是這樣的:

G1.算出現在所有的剩餘空間大小SUM,算出上一次空間分配到現在各個堆疊所增加的元素數量之和INC

G2.若SUM小於0,記憶體就滿了。否則在這裡設定兩個神奇的引數,α=0.1*SUM/n,β=0.9*SUM/INC。意思大概是α代表在10%的剩餘空間平均分配給每一個堆疊,β代表剩下的90%按全部堆疊從上一次空間分配到現在增長的總數量成比例地分成若干小份,這裡的α與β是小數。

G3.重新計算各個堆疊的基地址(空間的重新分配實質上就是基地址的重新分配),神奇的設定公式如下:NEWBASE[1]=BASE[1],也就是說第一個堆疊的起始位置是固定不變的,而對於其他堆疊j=2,3,……,n,x=y+α+D[j-1]*β,其中D[k]是棧k的增長數量,y的初始值為0,NEWBASE[j]=NEWBASE[j-1]+TOP[j-1]-BASE[j-1]+x(向下取整)-y(向下取整),y=x。可以看出堆疊之間一般來說都會有剩餘空間,因為存在10%的平均分配,而當堆疊的增長數量較大時,就會按比例從90%中分配更多空間給它,感覺這實際上是一種趨勢的分析,有迅速增長的趨勢,那就預留更多的空間。

G4.重新裝入各個元素。這裡的方法也有點小神奇。問題在於如何避免在元素平移的過程中不覆蓋掉有效的資料,可能出現覆蓋資料的情況有兩種,一是在元素向下平移時覆蓋掉後一個堆疊的資料,二是元素向上平移時覆蓋掉前一個堆疊的資料。對於向上平移的情況,假設要平移堆疊i,存在兩種可能,BASE[i-1]…NEWBASE[i-1]…NEWBASE[i]…BASE[i]…和NEWBASE[i-1]…BASE[i-1]…NEWBASE[i]…BASE[i]…,前一種情況,明顯NEWBASE[i-1]…NEWBASE[i]間的長度比BASE[i-1]…NEWBASE[i]的還要短,因此堆疊i-1裡面的元素肯定不會在NEWBASE[i]的後面,因此不會覆蓋;後一種情況,若堆疊i-1的元素足夠多的話,就有可能位於NEWBASE[i]的後面了,所以這裡用一種方法,從前面的堆疊開始進行向上平移,即先對堆疊i-1進行向上平移再對堆疊i進行平移。對於向下平移的情況有,BASE[i]…NEWBASE[i]…NEWBASE[i+1]…BASE[i+1]…和BASE[i]…NEWBASE[i]…BASE[i+1]…NEWBASE[i+1]…,分析是一樣的,可能出現覆蓋的就後一種情況,這時從後面的堆疊開始進行向下平移就可以了,就是說先對堆疊i+1向下平移再平移堆疊i。

上述方法的效能很難用準確的數學方法去描述,大概的規律是若總記憶體還有一半剩餘的時候效能是比較好的,需要重新分配空間的次數很少,當剩餘空間越來越少時效能會逐漸變壞。若記憶體已經所剩無幾了,堆疊溢位的情況會變得非常多,而且重新裝入元素花費的時間會變得很長,而實際上很多程式如果快要把記憶體用光了,那它一般很快就會真正地用光記憶體,這時候依然是出問題。這種情況下在程式快用光記憶體時對空間的重分配的消耗就是浪費的,因為最終還是會出問題。因此可以設定一個閾值,當剩餘空間低於這個閾值的時候就報錯,不再執行空間的重分配。