分配內存時如何減小內存碎片

  感受面試的時候常常會被問到這個問題,而後我也學習了一下Memcached的slab機制,發現不少服務器都是使用這種機制來分配內存,因此決定學習一下。面試

  首先,先對內存分配中的夥伴系統有初步的瞭解:算法

  在編程和使用的服務器軟件中,常常須要分配一組連續的頁框,而頻繁地申請和釋放不一樣大小的連續頁框,必然致使在已分配頁框的內存塊中分散了許多小塊的空閒頁框。這樣,即便這些頁框是空閒的,但要分配一個大塊的連續頁框就可能沒法知足。
  而Linux採用了夥伴系統來解決上述難題。把全部的空閒頁框分組爲11個塊鏈表,每一個塊鏈表分別包含大小爲1,2,4,8,16,32,64,128,256,512和1024個連續頁框的頁框塊。最大能夠申請1024個連續頁框,對應4MB大小的連續內存。每一個頁框塊的第一個頁框的物理地址是該塊大小的整數倍。例如,大小爲16個頁框的塊,其起始地址是16×212的倍數。
假設要申請一個256個頁框的塊,先從256個頁框的鏈表中查找空閒塊,若是沒有,就去512個頁框的鏈表中找,找到了則將頁框塊分爲2個256個頁框的塊,一個分配給應用,另一個移到256個頁框的鏈表中。若是512個頁框的鏈表中仍沒有空閒塊,繼續向1024個頁框的鏈表查找,若是仍然沒有,則返回錯誤。
頁框塊在釋放時,內核會主動將兩個互爲夥伴的頁框塊合併爲一個較大的頁框塊,成功後會試圖尋找夥伴併合併爲更大的內存塊,直至塊的大小超過上限或者沒有夥伴爲止。互爲夥伴的兩個內存塊必須符合如下條件:
  一、兩個塊具備相同的大小;
  二、兩個塊的物理地址連續;
  三、第一個快的物理地址是兩個塊大小的整數倍。
 
  slab分配機制則是對夥伴算法的改進,slab(Slab Allocation)的設計理念是基於對象緩衝的,基本想法是避免重複大量的初始化和清理操做。slab主要能夠用於頻繁非配釋放的內存對象。替代malloc/free
  改進的地方在於:

  它對內存區的處理並不須要進行初始化或回收。出於效率的考慮,Linux並不調用對象的構造或析構函數,而是把指向這兩個函數的指針都置爲空。Linux中引入Slab的主要目的是爲了減小對夥伴算法的調用次數。編程

     實際上,內核常常反覆使用某一內存區。例如,只要內核建立一個新的進程,就要爲該進程相關的數據結構(task_struct、打開文件對象等)分配內存區。當進程結束時,收回這些內存區。由於進程的建立和撤銷很是頻繁,所以,Linux的早期版本把大量的時間花費在反覆分配或回收這些內存區上。從Linux2.2開始,把那些頻繁使用的頁面保存在高速緩存中並從新使用。 能夠根據對內存區的使用頻率來對它分類。對於預期頻繁使用的內存區,能夠建立一組特定大小的專用緩衝區進行處理,以免內碎片的產生。對於較少使用的內存區,能夠建立一組通用緩衝區(如Linux2.0中所使用的2的冪次方)來處理,即便這種處理模式產生碎片,也對整個系統的性能影響不大。緩存

    硬件高速緩存的使用,又爲儘可能減小對夥伴算法的調用提供了另外一個理由,由於對夥伴算法的每次調用都會「弄髒」硬件高速緩存,所以,這就增長了對內存的平均訪問次數。服務器

Slab分配模式把對象分組放進緩衝區數據結構

  
  對於小對象, 就把Slab的描述結構slab_t放在該Slab中;對於大對象,則把Slab結構遊離出來,集中存放。關於Slab中的着色區再給予具體描述:
     每一個Slab的首部都有一個小小的區域是不用的,稱爲「着色區(coloring area)」。着色區的大小使Slab中的每一個對象的起始地址都按高速緩存中的」緩存行(cache line)」大小進行對齊(80386的一級高速緩存行大小爲16字節,Pentium爲32字節)。由於Slab是由1個頁面或多個頁面(最多爲32)組成,所以,每一個Slab都是從一個頁面邊界開始的,它天然按高速緩存的緩衝行對齊。可是,Slab中的對象大小不肯定,設置着色區的目的就是將Slab中第一個對象的起始地址日後推到與緩衝行對齊的位置。由於一個緩衝區中有多個Slab,所以,應該把每一個緩衝區中的各個Slab着色區的大小盡可能安排成不一樣的大小,這樣可使得在不一樣的Slab中,處於同一相對位置的對象,讓它們在高速緩存中的起始地址相互錯開,這樣就能夠改善高速緩存的存取效率。
     每一個Slab上最後一個對象之後也有個小小的廢料區是不用的,這是對着色區大小的補償,其大小取決於着色區的大小,以及Slab與其每一個對象的相對大小。但該區域與着色區的總和對於同一種對象的各個Slab是個常數。
     每一個對象的大小基本上是所需數據結構的大小。只有當數據結構的大小不與高速緩存中的緩衝行對齊時,才增長若干字節使其對齊。因此,一個Slab上的全部對象的起始地址都必然是按高速緩存中的緩衝行對齊的。
 
參考: 深刻分析 Linux 內核源碼 http://oss.org.cn/kernel-book/
     Memcached 源碼分析
相關文章
相關標籤/搜索