複製鏈接
請複製以下鏈接發送給好友

內存碎片

鎖定
採用分區式存儲管理的系統,在儲存分配過程中產生的、不能供用户作業使用的主存裏的小分區稱成“內存碎片”。內存碎片分為內部碎片和外部碎片。
中文名
內存碎片
外文名
memory fragmentation
説    明
不能被利用的內存空間
處    於
區域內部或頁面內部
性    質
存儲塊

內存碎片產生

內存分配有靜態分配和動態分配兩種。
靜態分配在程序編譯鏈接時分配的大小和使用壽命就已經確定,而應用上要求操作系統可以提供給進程運行時申請和釋放任意大小內存的功能,這就是內存的動態分配。
因此動態分配將不可避免會產生內存碎片的問題,那麼什麼是內存碎片?內存碎片即“碎片的內存”描述一個系統中所有不可用的空閒內存,這些碎片之所以不能被使用,是因為負責動態分配內存的分配算法使得這些空閒的內存無法使用,這一問題的發生,原因在於這些空閒內存以小且不連續方式出現在不同的位置。因此這個問題的或大或小取決於內存管理算法的實現上。
為什麼會產生這些小且不連續的空閒內存碎片呢?
實際上這些空閒內存碎片存在的方式有兩種:a.內部碎片 b.外部碎片 。
內部碎片的產生:因為所有的內存分配必須起始於可被 4、8 或 16 整除(視處理器體系結構而定)的地址或者因為MMU的分頁機制的限制,決定內存分配算法僅能把預定大小的內存塊分配給客户。假設當某個客户請求一個 43 字節的內存塊時,因為沒有適合大小的內存,所以它可能會獲得 44字節、48字節等稍大一點的字節,因此由所需大小四捨五入而產生的多餘空間就叫內部碎片。
外部碎片的產生: 頻繁的分配與回收物理頁面會導致大量的、連續且小的頁面塊夾雜在已分配的頁面中間,就會產生外部碎片。假設有一塊一共有100個單位的連續空閒內存空間,範圍是0~99。如果你從中申請一塊內存,如10個單位,那麼申請出來的內存塊就為0~9區間。這時候你繼續申請一塊內存,比如説5個單位大,第二塊得到的內存塊就應該為10~14區間。如果你把第一塊內存塊釋放,然後再申請一塊大於10個單位的內存塊,比如説20個單位。因為剛被釋放的內存塊不能滿足新的請求,所以只能從15開始分配出20個單位的內存塊。現在整個內存空間的狀態是0~9空閒,10~14被佔用,15~34被佔用,25~99空閒。其中0~9就是一個內存碎片了。如果10~14一直被佔用,而以後申請的空間都大於10個單位,那麼0~9就永遠用不上了,變成外部碎片。 [1] 

內存碎片分類

內存碎片分為:內部碎片和外部碎片。

內存碎片內部碎片

內部碎片就是已經被分配出去(能明確指出屬於哪個進程)卻不能被利用的內存空間;
內部碎片是處於區域內部或頁面內部的存儲塊。佔有這些區域或頁面的進程並不使用這個存儲塊。而在進程佔有這塊存儲塊時,系統無法利用它。直到進程釋放它,或進程結束時,系統才有可能利用這個存儲塊。
單道連續分配只有內部碎片。多道固定連續分配既有內部碎片,又有外部碎片。

內存碎片外部碎片

外部碎片指的是還沒有被分配出去(不屬於任何進程),但由於太小了無法分配給申請內存空間的新進程的內存空閒區域。
外部碎片是出於任何已分配區域或頁面外部的空閒存儲塊。這些存儲塊的總和可以滿足當前申請的長度要求,但是由於它們的地址不連續或其他原因,使得系統無法滿足當前申請。
多道可變連續分配只有外部碎片 [2] 

內存碎片減少方法

內存碎是因為在分配一個內存塊後,使之空閒,但不將空閒內存歸還給最大內存塊而產生的。最後這一步很關鍵。如果內存分配程序是有效的,就不能阻止系統分配內存塊並使之空閒。即使一個內存分配程序不能保證返回的內存能與最大內存塊相連接(這種方法可以徹底避免內存碎片問題),但你可以設法控制並限制內存碎片。所有這些作法涉及到內存塊的分割。每當系統減少被分割內存塊的數量,確保被分割內存塊儘可能大時,你就會有所改進。
這樣做的目的是儘可能多次反覆使用內存塊,而不要每次都對內存塊進行分割,以正好符合請求的存儲量。分割內存塊會產生大量的小內存碎片,猶如一堆散沙。以後很難把這些散沙與其餘內存結合起來。比較好的辦法是讓每個內存塊中都留有一些未用的字節。留有多少字節應看系統要在多大程度上避免內存碎片。對小型系統來説,增加幾個字節的內部碎片是朝正確方向邁出的一步。當系統請求1字節內存時,你分配的存儲量取決於系統的工作狀態。
如果系統分配的內存存儲量的主要部分是 1 ~ 16 字節,則為小內存也分配 16 字節是明智的。只要限制可以分配的最大內存塊,你就能夠獲得較大的節約效果。但是,這種方法的缺點是,系統會不斷地嘗試分配大於極限的內存塊,這使系統可能會停止工作。減少最大和最小內存塊存儲量之間內存存儲量的數量也是有用的。採用按對數增大的內存塊存儲量可以避免大量的碎片。例如,每個存儲量可能都比前一個存儲量大 20%。在嵌入式系統中採用“一種存儲量符合所有需要”對於嵌入式系統中的內存分配程序來説可能是不切實際的。這種方法從內部碎片來看是代價極高的,但系統可以徹底避免外部碎片,達到支持的最大存儲量。
將相鄰空閒內存塊連接起來是一種可以顯著減少內存碎片的技術。如果沒有這一方法,某些分配算法(如最先適合算法)將根本無法工作。然而,效果是有限的,將鄰近內存塊連接起來只能緩解由於分配算法引起的問題,而無法解決根本問題。而且,當內存塊存儲量有限時,相鄰內存塊連接可能很難實現。
有些內存分配器很先進,可以在運行時收集有關某個系統的分配習慣的統計數據,然後,按存儲量將所有的內存分配進行分類,例如分為小、中和大三類。系統將每次分配指向被管理內存的一個區域,因為該區域包括這樣的內存塊存儲量。較小存儲量是根據較大存儲量分配的。這種方案是最先適合算法和一組有限的固定存儲量算法的一種有趣的混合,但不是實時的。
有效地利用暫時的侷限性通常是很困難的,但值得一提的是,在內存中暫時擴展共處一地的分配程序更容易產生內存碎片。儘管其它技術可以減輕這一問題,但限制不同存儲量內存塊的數目仍是減少內存碎片的主要方法。
現代軟件環境業已實現各種避免內存碎片的工具。例如,專為分佈式高可用性容錯系統開發的 OSE 實時操作系統可提供三種運行時內存分配程序:內核 alloc(),它根據系統或內存塊池來分配;堆 malloc(),根據程序堆來分配; OSE 內存管理程序 alloc_region,它根據內存管理程序內存來分配。
從 許多方面來看,Alloc就是終極內存分配程序。它產生的內存碎片很少,速度很快,並有判定功能。你可以調整甚至去掉內存碎片。只是在分配一個存儲量後,使之空閒,但不再分配時,才會產生外部碎片。內部碎片會不斷產生,但對某個給定的系統和八種存儲量來説是恆定不變的。
Alloc 是一種有八個自由表的固定存儲量內存分配程序的實現方法。系統程序員可以對每一種存儲量進行配置,並可決定採用更少的存儲量來進一步減少碎片。除開始時以外,分配內存塊和使內存塊空閒都是恆定時間操作。首先,系統必須對請求的存儲量四捨五入到下一個可用存儲量。就八種存儲量而言,這一目標可用三個 如果 語句來實現。其次,系統總是在八個自由表的表頭插入或刪除內存塊。開始時,分配未使用的內存要多花幾個週期的時間,但速度仍然極快,而且所花時間恆定不變。
堆 malloc() 的內存開銷(8 ~ 16 字節/分配)比 alloc小,所以你可以停用內存的專用權。malloc() 分配程序平均來講是相當快的。它的內部碎片比alloc()少,但外部碎片則比alloc()多。它有一個最大分配存儲量,但對大多數系統來説,這一極限值足夠大。可選的共享所有權與低開銷使 malloc() 適用於有許多小型對象和共享對象的 C++ 應用程序。堆是一種具有內部堆數據結構的夥伴系統的實現方法。在 OSE 中,有 28 個不同的存儲量可供使用,每種存儲量都是前兩種存儲量之和,於是形成一個斐波那契(Fibonacci)序列。實際內存塊存儲量為序列數乘以 16 字節,其中包括分配程序開銷或者 8 字節/分配(在文件和行信息啓用的情況下為 16 字節)。
當你很少需要大塊內存時,則OSE內存管理程序最適用。典型的系統要把存儲空間分配給整個系統、堆或庫。在有 MMU 的系統中,有些實現方法使用 MMU 的轉換功能來顯著降低甚至消除內存碎片。在其他情況下,OSE 內存管理程序會產生非常多的碎片。它沒有最大分配存儲量,而且是一種最先適合內存分配程序的實現方法。內存分配被四捨五入到頁面的偶數——典型值是 4 k 字節。
參考資料