歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

glibc內存管理

X86平台LINUX進程內存布局如下:

  上面個段的含義如下:

  text:存放程序代碼的,編譯時確定,只讀;

  data:存放程序運行時就能確定的數據,可讀可寫;

  bss:定義而沒有初始化的全局變量和靜態變量;

  heap:一般由程序員分配,如果不釋放的話在程序結束的時候可能被OS回收;

  stack:有編譯器自動分配釋放,存放函數的參數、局部變量等;

  Mmap:映射區域;

  程序可以直接使用系統調用來管理heap和mmap,但更多的時候是使用C提供的malloc和free來動態地分配和釋放內存。Linux上的stack的限制大致是8M,而在Windows上為2M.

  C風格的內存管理程序:

  也就是malloc和free,主要是通過brk或者mmap添加額外的虛擬內存。對於那些需要保持長期存儲的程序使用malloc來管理內存可能會非常令人失望,如果有大量的不固定的內存引用,經常難以知道他們如何被釋放。

  池式內存管理:

  應用程序可以更簡單地管理內存;內存分配和回收更快;可以預先分配錯誤處理池,以便程序在常規內存被耗盡時仍然可以恢復;有非常易於使用的標准實現。

  內存池只適用於操作可以分階段的程序;通常不能和第三方庫很好滴合作;如果程序的結構發生變化則不得不修改內存池;必須記住需要從哪個池進行分配。

  引用計數:

  不能忘記調用引用計數函數;無法釋放作為循環數據結構的一部分;在多線程環境中更難也更慢。

  垃圾回收:

  永遠不必擔心內存的雙重釋放或者對象的生命周期;

  無法干涉何時釋放內存;比其他形式的內才能管理更慢;如果忘記將不再使用的指針設置為null,

  內存管理器的設計目標:

  最大化兼容性;

  最大化可移植性(能很好地和OS交流);

  浪費最小的空間(管理自身的數據結構也是需要內存的,還有一個需要注意的是碎片);

  最快的速度(2/8原則,主要用來優化熱點);

  最大化可調性(能適應多種分配的需求,或者是通過配置來適應);

  最大化局部性(這裡考慮的是CPU的cache和內存之間的關系);

  最大化調試功能(作為程序員就不用說了);

  最大化適應性(在不修改配置時候的適應性);

  ptmalloc實現了malloc和free以及一組其他函數,以提供動態內存管理的支持。分配器處於用戶程序和內核之間,用來響應用戶的分配請求,向操作系統申請內存,然後將其返回給用戶程序。為了保持高效的分配,分配器一般都會預先分配一大塊內存,並通過某種算法管理。用戶釋放掉的內存也並不是立即就返回給操作系統。ptmalloc在設計的時候折中了高效性、高空間利用率、高可用性等設計目標。其設計假設如下:

  用mmap來分配長生命周期的大內存;

  特別大的內存分配總是使用mmap;

  短生命周期的內存分配使用brk;

  盡量只緩存小的、臨時使用的內存,而大的內存則直接歸還給系統;

  小內存塊只有在malloc和free的時候進行合並;

  收縮堆的條件是當前free的快的大小加上前後能合並的chunk的大小大於64K,並且堆頂的大小達到閥值;

  需要長期存儲的程序不適合ptmalloc;

  總體上的結構如下:

  而實際存在數據的是Chunk,使用中的Chunk的結構如下:

  其中:

  P:表示前一個Chunk是否在使用中;

  M:標志Chunk是從那個內存區域獲得的虛擬內存;

  A:標志是否是主分配區;

  空閒的Chunk在內存中的結構如下:

  在glibc中使用bin來管理空閒的chunk,細節就不說了。當空閒的chunk被鏈接到bin中的時候,ptmalloc會檢查他前後的chunk是否也是空閒的,如果是的話,就會合並成一個大的chunk.bin的結構如下:

  ptmalloc為了提高分配的速度,會把一些小的chunk先放到Fast bins中。fast bins中的chunk並不改變它的使用標志P,這樣也就無法將他們合並,當用戶分配小的內存的時候,ptmalloc首先會在fast bins中查找響應的空閒塊,然後再去查找Unsorted bins中空閒的chunk.被合並後的chunk、或者是不能放在fast bins中的chunk會首先放在Unsorted bin中,如果在分配的時候在Unsorted bin中無法滿足要求,則將Unsorted bin中的chunk加入到bins中。


 因為在分配內存的時候是用低地址到高地址分配的,這樣一個分配到的大的內存(用來模擬sub-heap)的上面很有可能是有一塊空閒的內存,也就是Top chunk.Top chunk是在fast bin和bins之後才考慮的,所以這段區間並不在bins這些結構中。如果ptmalloc設法在top chunk中分配一段空間時且top chunk不夠大,這時會重新分配一個新的sub-heap,並將top chunk遷移到新的sub-heap上。新的sub-heap與已有的sub-heap用單向鏈表連接起來。如下:

  當要分配的空間足夠大的時候,ptmalloc會使用mmap來直接使用內存映射來講頁映射到進程空間。這樣分配的chunk在被free時將直接接觸映射,再次對這樣的內存區域的引用將會引起段錯誤。

  Last remainder chunk是另外一種特殊的chunk,在分配一個small chunk的時候,如果在small bins中找不到合適的chunk,如果last remainder chunk的大小大於所需的small chunk大小。那麼它將會分裂成兩個,一個供用戶使用,另一個變成了新的Last remainder chunk.

  ptmalloc的響應用戶內存分配請求的具體步驟:

  獲取主分配區的鎖,如果失敗就查找非主分配區,再不行就創建新的非主分配區;

  將用戶請求的大小轉換為實際需要分配的chunk空間的大小;

  如果chunk_size<=max_fast則轉4,否則,跳轉5;

  嘗試在fast bins中分配,如果成功則結束返回;

  如果chunk_size<=512B則下一步,否則跳轉6;

  查找對應的small bins,如果找到則分配成功;否則轉7;

  合並fast bins中的chunk,遍歷unsorted bin中的chunk,如果只有一個chunk,並且這個chunk在上次的分配中被使用過,並且所需分配的chunk大小屬於smallbins,並且chunk的大小滿足要求,則直接將該chunk進行切割,分配結束;否則將其放入bins;

  在large bins中查找;如果失敗轉9;

  如果top chunk滿足要求,則從中分配出一塊;否則轉10;

  如果是主分配區,調用sbrk增加top chunk的大小;否則分配一個新的sub-heap;或者直接使用mmap來分配;如果需要用mmap分配轉11,否則轉12;

  使用mmap系統調用為程序的內存空間映射一塊空間,然後將指針返回給用戶程序;

  判斷是否第一次調用malloc,若是主分配區,則進行一次初始化工作。否則根據10的規則來分配。

  為了避免Glibc內存暴增,使用時需要注意一下幾點:

  後分配的內存先釋放(top chunk的考慮);

  不適合用於管理長生命周期的內存,特別是持續不定期分配和釋放長生命周期的內存;

  不要關閉ptmalloc的mmap分配閥值動態調整機制;

  多線程分階段執行的程序不適合使用ptmalloc(更適合使用內存池);

  盡量減少程序的線程數量和避免頻繁分配、釋放內存;

Copyright © Linux教程網 All Rights Reserved