在使用早期的計算機上編寫程序時,有關數據存儲在什麼位置等這樣的問題都是須要程序員本身來給數據分配內存。程序員
而如今的高級語言,大大的減小了程序員的工做,不須要直接和存儲空間打交道,程序在編譯時由編譯程序去合理地分配空間。編程
本文重點解決的問題是:學習
✪ 對於用戶向系統提出的申請空間的請求,系統如何分配內存?spa
✪ 當用戶不在使用以前申請的內存空間後,系統又如何回收?3d
這裏的用戶,不是普通意義上的用戶,多是一個普通的變量,一個應用程序,一個命令等等。只要是向系統發出內存申請的,均可以稱之爲用戶。指針
對於計算機中的內存來講,稱已經分配給用戶的的內存區統稱爲「佔用塊」;還未分配出去的內存區統稱爲「空閒塊」或者「可利用空間塊」。視頻
對於初始狀態下的內存來講,整個空間都是一個空閒塊(在編譯程序中稱爲「堆」)。可是隨着不一樣的用戶不斷地提出存儲請求,系統依次分配。blog
整個內存區就會分割成兩個大部分:低地址區域會產生不少佔用塊;高地址區域仍是空閒塊。以下圖所示:排序
可是當某些用戶運行結束,所佔用的內存區域就變成了空閒塊,以下圖所示:ip
此時,就造成了佔用塊和空閒塊犬牙交錯的狀態。當後續用戶請求分配內存時,系統有兩種分配方式:
一、系統繼續利用高地址區域的連續空閒塊分配給用戶,不去理會以前分配給用戶的內存區域的狀態。直到分配沒法進行,也就是高地址的空閒塊不能知足用戶的需求時,系統纔會去回收以前的空閒塊,從新組織繼續分配;
二、當用戶運行一結束,系統立刻將其所佔空間進行回收。當有新的用戶請求分配內存時,系統遍歷全部的空閒塊,從中找出一個合適的空閒塊分配給用戶。
合適的空閒塊指的是可以知足用戶要求的空閒塊,具體的查找方式有多種,後續會介紹。
當採用第 2 種方式時,系統須要創建一張記錄全部空閒塊信息的表。表的形式有兩種:目錄表和鏈表。各自的結構以下圖所示:
目錄表:表中每一行表明一個空閒塊,由三部分組成:
✪ 初始地址:記錄每一個空閒塊的起始地址。
✪ 空閒塊大小:記錄每一個空閒塊的內存大小。
✪ 使用狀況:記錄每一個空閒塊是否存儲被佔用的狀態。
鏈表:表中每一個結點表明一個空閒塊,每一個結點中須要記錄空閒塊的使用狀況、大小和鏈接下一個空閒塊的指針域。
因爲鏈表中有指針的存在,因此結點中不須要記錄各內存塊的起始地址。
————————
系統在不一樣的環境中運行,根據用戶申請空間的不一樣,存儲空閒塊的可利用空間表有如下不一樣的結構:
一、若是每次用戶請求的存儲空間大小相同,對於此類系統中的內存來講,在用戶運行初期就將整個內存存儲塊按照所需大小進行分割,而後經過鏈表連接。當用戶申請空間時,從鏈表中摘除一個結點歸其使用;用完後再連接到可利用空間表上。
二、每次若是用戶申請的都是若干種大小規格的存儲空間,針對這種狀況能夠創建若干個可利用空間表,每個鏈表中的結點大小相同。當用戶申請某一規格大小的存儲空間時,就從對應的鏈表中摘除一個結點供其使用;用完後連接到相同規格大小的鏈表中。
三、用戶申請的內存的大小不固定,因此形成系統分配的內存塊的大小也不肯定,回收時,連接到可利用空間表中每一個結點的大小也各不同。
第 2 種狀況下容易面臨的問題是:若是同用戶申請空間大小相同的鏈表中沒有結點時,就須要找結點更大的鏈表,從中取出一個結點,一部分給用戶使用,剩餘部分插入到相應大小的鏈表中;回收時,將釋放的空閒塊插入到大小相同的鏈表中去。若是沒有比用戶申請的內存空間相等甚至更大的結點時,就須要系統從新組織一些小的連續空間,而後給用戶使用。
一般狀況下系統中的可利用空間表是第 3 種狀況。如圖 3(C) 所示。因爲鏈表中各結點的大小不一,在用戶申請內存空間時,就須要從可利用空間表中找出一個合適的結點,有三種查找的方法:
✪ 首次擬合法:在可利用空間表中從頭開始依次遍歷,將找到的第一個內存不小於用戶申請空間的結點分配給用戶,剩餘空間仍留在鏈表中;回收時只要將釋放的空閒塊插入在鏈表的表頭便可。
✪ 最佳擬合法:和首次擬合法不一樣,最佳擬合法是選擇一塊內存空間不小於用戶申請空間,可是卻最接近的一個結點分配給用戶。爲了實現這個方法,首先要將鏈表中的各個結點按照存儲空間的大小進行從小到大排序,由此,在遍歷的過程當中只須要找到第一塊大於用戶申請空間的結點便可進行分配;用戶運行完成後,須要將空閒塊根據其自身的大小插入到鏈表的相應位置。
✪ 最差擬合法:和最佳擬合法正好相反,該方法是在不小於用戶申請空間的全部結點中,篩選出存儲空間最大的結點,從該結點的內存空間中提取出相應的空間給用戶使用。爲了實現這一方法,能夠在開始前先將可利用空間表中的結點按照存儲空間大小從大到小進行排序,第一個結點天然就是最大的結點。回收空間時,一樣將釋放的空閒塊插入到相應的位置上。
以上三種方法各有所長:
✪ 最佳擬合法因爲每次分配相差不大的結點給用戶使用,因此會生成不少存儲空間特別小的結點,以致於根本沒法使用,使用過程當中,鏈表中的結點存儲大小發生兩極分化,大的很大,小的很小。該方法適用於申請內存大小範圍較廣的系統
✪ 最差擬合法,因爲每次都是從存儲空間最大的結點中分配給用戶空間,因此鏈表中的結點大小不會起伏太大。依次適用於申請分配內存空間較窄的系統。
✪ 首次擬合法每次都是隨機分配。在不清楚用戶申請空間大小的狀況下,使用該方法分配空間。
同時,三種方法中,最佳擬合法相比於其它兩種方式,不管是分配過程仍是回收過程,都須要遍歷鏈表,全部最費時間。
不管使用以上三種分配方式中的哪種,最終內存空間都會成爲一個一個特別小的內存空間,對於用戶申請的空間的需求,單獨拿出任何一個結點都不可以知足。
可是並非說整個內存空間就不夠用戶使用。在這種狀況下,就須要系統在回收的過程考慮將地址相鄰的空閒塊合併。
最後,無論你是轉行也好,初學也罷,進階也可,若是你想學編程~
【值得關注】個人 C/C++編程學習交流俱樂部【點擊進入】
問題答疑,學習交流,技術探討,還有超多編程資源大全,零基礎的視頻也超棒~