1. 程式人生 > >記憶體管理-夥伴系統

記憶體管理-夥伴系統

問題重述:

固定和可變分割槽記憶體管理方法都存在缺陷。固定分割槽由於分割槽數目是固定的,因此限 制了活動程序的個數,而且當可用記憶體大小與程序記憶體需求大小不匹配時,記憶體使用效率非 常低效。而可變分割槽方法管理起來較為複雜,而且由於程序映象的上下浮動會帶來額外開銷。 夥伴系統是二者的一種折中,兼具固定和可變分割槽的優點。

在夥伴系統中,記憶體塊個數和大小是不固定的,但是大小隻能限定為 2K 個位元組,其中 L ≤ K≤ U

l    2L = 最小塊的大小(位元組)

l    2U =  最大塊的大小(位元組)。通常是整個記憶體的大小。 開始時,整個可供記憶體分配的空間被看做一個塊,其大小為 2U。如果請求的記憶體大小為 s, 且 2U-1 < s ≤ 2U,那麼把整個記憶體,即 2U 分配給它。否則將整個記憶體空間分為兩個大小為 2U-1 的夥伴塊,並將其中的一個暫時分配給該請求。然後再考查,該 2U-1 大小的塊中,條件

2U-2 <s ≤2U-1 是否滿足,若滿足,則將該 2U-1 大小的塊正式分配給該請求,然後退出記憶體分 配演算法;若不滿足,則將該 2U-1 大小的塊繼續一份為二,產生兩個大小為 2U-2 的夥伴塊,並 把其中一個暫時分配給該請求。這樣的過程持續下去,直到如下條件滿足時,記憶體分配演算法 退出:

(1) 存在 L< i ≤ U,使得 2i-1 <s ≤ 2i 時,將大小為 2i 的塊分配給該請求,演算法退出;

(2) s≤ 2L 時,把大小為 2L 的塊分配給該請求,演算法退出。

一、類成員變數說明

添加了一個新的靜態成員變數,賦初值為1,用於表示當前分割槽節點的編號

static int number =1;

        public int ID; //分割槽編號
	public int status =0; //分割槽狀態,1 表示已分配,0 表示空閒
	public int size =0; //分割槽大小,只能為2L ~ 2U
	public Chunk leftBuddy = null ; //左夥伴
	public Chunk rightBuddy = null ;//右夥伴
	static int number =1;

二、類成員方法說明

public Chunk();

類的構造方法,每次在生成新的節點時呼叫構造方法給當前的節點編號,實現每個節點都有唯一的一個編號

public Chunk(){
		this.ID=this.number;
		this.number++;
	}

public void printMemMap();

遍歷二叉樹的所有節點,將二叉樹的分割槽號、分割槽狀態、分割槽大小輸出到控制檯中。

基本思想:採用先序遍歷的方法將每一個節點的資訊打印出來,對於當前節點的葉子節點不為空則列印葉子節點,以此類推

	public void printMemMap() {
		System.out.println("分割槽號:"+this.ID);
		System.out.println("分割槽狀態:"+this.status);
		System.out.println("分割槽大小:"+this.size);
		System.out.println();
		if(this.leftBuddy != null) {
			this.leftBuddy.printMemMap();
			this.rightBuddy.printMemMap();
		}
		

	}

public int Release(int id);

釋放指定節點所佔用的記憶體,採用遍歷的方法找到需要釋放記憶體的節點,當釋放該節點後判斷該節點的兄弟節點是否也為空,是則合併這兩個節點,否則不能合併

注意:因為在合併時需要刪除當前節點和其兄弟節點,所以應當用其父節點來訪問當前節點,否則不能夠刪除當前節點和其兄弟節點

//釋放記憶體並判斷合併節點
	public int Release(int id) {
		// 釋放記憶體
		if(this.leftBuddy.ID == id) 
			this.leftBuddy.status=0;
		
		if(this.rightBuddy.ID == id)
			this.rightBuddy.status=0;

		// 合併
		if (this.leftBuddy.status==0&&this.rightBuddy.status==0) {
			this.status=0;
			this.leftBuddy = null;
			this.rightBuddy = null;
			System.out.println("合併節點成功");
			return 0;
		}
		// 不能合併
		else {
			System.out.println("不能合併節點");
			return -1;
		}
	
	}

public int Allocate(int size);

根據所需要的記憶體大小動態分配記憶體

基本思想:

首先判斷需要分配的記憶體大小是否在該節點大小的一半到最大值之間,如果是則分配該節點,否則將該節點分成大小小相等的兩個節點,再用子節點來分配,一直重複之前的操作,知道子節點的記憶體大小為1k)時。

注意:再分配過程中需要考慮到優先分配左節點還是右節點,只有當做節點都不能滿足分配條件時才將右節點分解,再進行分配

	public int Allocate(int size) {
		
		if(this.status == 0){//父節點未被佔用
			if(size>this.size/2 && size<=this.size){//大於空間大小的一半,小於該空間大小
				this.status=1;	
				return this.ID;
			}
			else if(size<this.size/2 && this.size/2>=1){//比空間大小一半要小,空間折半後至少為1k
				int flag;
				this.status=1;
				this.leftBuddy=new Chunk();
				this.rightBuddy=new Chunk();
				this.leftBuddy.size=this.size/2;
				this.rightBuddy.size=this.size/2;
				flag=this.leftBuddy.Allocate(size);
				return flag;
			}
			else
				return -1;

		}
		else if(this.leftBuddy !=null && this.rightBuddy!=null){//父節點被佔用,但子節點有未被佔用的
			int flagl,flagr;
			flagl=this.leftBuddy.Allocate(size);
			if(flagl>0)
				return flagl;
			else{
				flagr =this.rightBuddy.Allocate(size);
				if(flagr >0)
					return flagr;
				else
					return -1;
			}
		}
		else//父節點被佔用,且子節點也被佔用		
			return -1;	
		
	}
	

因為演算法不夠優化,還存在一些問題,所以得到的結果和手工計算的還有差別,當在進行小量分配時不會出現錯誤(和手工計算一樣),當二叉樹的深度較大時會出現一些誤差(和手工分配不一樣)