linux malloc內存申請相關參數設置

情況一、malloc小於128k的內存

malloc小於128k的內存時使用brk分配內存,將_edata往高地址推(只分配虛擬空間,不對應物理內存(因此沒有初始化),第一次讀/寫數據時,引起內核缺頁中斷,內核才分配對應的物理內存,然後虛擬地址空間建立映射關係),如下圖:

1、進程啓動的時候,其(虛擬)內存空間的初始佈局如圖1所示。

其中,mmap內存映射文件是在堆和棧的中間(例如libc-2.2.93.so,其它數據文件等),爲了簡單起見,省略了內存映射文件。
_edata指針(glibc裏面定義)指向數據段的最高地址。

2、進程調用A=malloc(30K)以後,內存空間如圖2:
malloc函數會調用brk系統調用,將_edata指針往高地址推30K,就完成虛擬內存分配。
你可能會問:只要把_edata+30K就完成內存分配了?

事實是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊內存現在還是沒有物理頁與之對應的,等到進程第一次讀寫A這塊內存的時候,發生缺頁中斷,這個時候,內核才分配A這塊內存對應的物理頁。也就是說,如果用malloc分配了A這塊內容,然後從來不訪問它,那麼,A對應的物理頁是不會被分配的。

3、進程調用B=malloc(40K)以後,內存空間如圖3。

在這裏插入圖片描述

情況二、malloc大於128k的內存

malloc大於128k的內存,使用mmap分配內存,在堆和棧之間找一塊空閒內存分配(對應獨立內存,而且初始化爲0),如下圖:

在這裏插入圖片描述
4、進程調用C=malloc(200K)以後,內存空間如圖4:
默認情況下,malloc函數分配內存,如果請求內存大於128K(可由M_MMAP_THRESHOLD選項調節),那就不是去推_edata指針了,而是利用mmap系統調用,從堆和棧的中間分配一塊虛擬內存。
這樣子做主要是因爲:
brk分配的內存需要等到高地址內存釋放以後才能釋放(例如,在B釋放之前,A是不可能釋放的,這就是內存碎片產生的原因,什麼時候緊縮看下面),而mmap分配的內存可以單獨釋放。
當然,還有其它的好處,也有壞處,再具體下去,有興趣的同學可以去看glibc裏面malloc的代碼了。

5 、進程調用D=malloc(100K)以後,內存空間如圖5;
6 、進程調用free©以後,C對應的虛擬內存和物理內存一起釋放。
7、進程調用free(B)以後,如圖7所示:
B對應的虛擬內存和物理內存都沒有釋放,因爲只有一個_edata指針,如果往回推,那麼D這塊內存怎麼辦呢?

當然,B這塊內存,是可以重用的,如果這個時候再來一個40K的請求,那麼malloc很可能就把B這塊內存返回回去了。

8、進程調用free(D)以後,如圖8所示:
B和D連接起來,變成一塊140K的空閒內存。

9、默認情況下:
當最高地址空間的空閒內存超過128K(可由M_TRIM_THRESHOLD選項調節)時,執行內存緊縮操作(trim)。在上一個步驟free的時候,發現最高地址空閒內存超過128K,於是內存緊縮,變成圖9所示。

內存相關參數設置接口

mallopt函數可以控制 內存分配的函數:

int mallopt(int param,int value)//控制 內存分配的函數 。

param 的取值可以爲M_CHECK_ACTION、M_MMAP_MAX、M_MMAP_THRESHOLD、M_MXFAST(從glibc2.3起)、M_PERTURB(從glibc2.4起)、M_TOP_PAD、M_TRIM_THRESHOLD。

此處解釋param取值爲M_MXFAST的情況;

value是以 字節爲單位的。

比如設置M_MMAP_THRESHOLD選項可以設置啓用mmap申請malloc字節數閥值,設置-1是不啓用mmap
這些選項可以通過mallopt()進行設置:

1. M_MXFAST
M_MXFAST用於設置fast bins中保存的chunk的最大大小,默認值爲64B,fast bins中保存的chunk在一段時間內不會被合併,分配小對象時可以首先查找fast bins,如果fast bins找到了所需大小的chunk,就直接返回該chunk,大大提高小對象的分配速度,但這個值設置得過大,會導致大量內存碎片,並且會導致ptmalloc緩存了大量空閒內存,去不能歸還給操作系統,導致內存暴增。

M_MXFAST的最大值爲80B,不能設置比80B更大的值,因爲設置爲更大的值並不能提高分配的速度。Fast bins是爲需要分配許多小對象的程序設計的,比如需要分配許多小struct,小對象,小的string等等。

如果設置該選項爲0,就會不使用fast bins。

2. M_TRIM_THRESHOLD
M_TRIM_THRESHOLD用於設置mmap收縮閾值,默認值爲128KB。自動收縮只會在free時才發生,如果當前free的chunk大小加上前後能合併chunk的大小大於64KB,並且top chunk的大小達到mmap收縮閾值,對於主分配區,調用malloc_trim()返回一部分內存給操作系統,對於非主分配區,調用heap_trim()返回一部分內存給操作系統,在發生內存收縮時,還是從新設置mmap分配閾值和mmap收縮閾值。
這個選項一般與M_MMAP_THRESHOLD選項一起使用,M_MMAP_THRESHOLD用於設置mmap分配閾值,對於長時間運行的程序,需要對這兩個選項進行調優,儘量保證在ptmalloc中緩存的空閒chunk能夠得到重用,儘量少用mmap分配臨時用的內存。不停地使用系統調用mmap分配內存,然後很快又free掉該內存,這樣是很浪費系統資源的,並且這樣分配的內存的速度比從ptmalloc的空閒chunk中分配內存慢得多,由於需要頁對齊導致空間利用率降低,並且操作系統調用mmap()分配內存是串行的,在發生缺頁異常時加載新的物理頁,需要對新的物理頁做清0操作,大大影響效率。
M_TRIM_THRESHOLD的值必須設置爲頁大小對齊,設置爲-1會關閉內存收縮設置。
注意:試圖在程序開始運行時分配一塊大內存,並馬上釋放掉,以期望來觸發內存收縮,這是不可能的,因爲該內存馬上就返回給操作系統了。

3. M_MMAP_THRESHOLD
M_MMAP_THRESHOLD用於設置mmap分配閾值,默認值爲128KB,ptmalloc默認開啓動態調整mmap分配閾值和mmap收縮閾值。

當用戶需要分配的內存大於mmap分配閾值,ptmalloc的malloc()函數其實相當於mmap()的簡單封裝,free函數相當於munmap()的簡單封裝。相當於直接通過系統調用分配內存,回收的內存就直接返回給操作系統了。因爲這些大塊內存不能被ptmalloc緩存管理,不能重用,所以ptmalloc也只有在萬不得已的情況下才使用該方式分配內存。

但使用mmap分配有如下的好處:

l Mmap的空間可以獨立從系統中分配和釋放的系統,對於長時間運行的程序,申請長生命週期的大內存塊就很適合有這種方式。

l Mmap的空間不會被ptmalloc鎖在緩存的chunk中,不會導致ptmalloc內存暴增的問題。

l 對有些系統的虛擬地址空間存在洞,只能用mmap()進行分配內存,sbrk()不能運行。

使用mmap分配內存的缺點:

l 該內存不能被ptmalloc回收再利用。

l 會導致更多的內存浪費,因爲mmap需要按頁對齊。

l 它的分配效率跟操作系統提供的mmap()函數的效率密切相關,Linux系統強制把匿名mmap的內存物理頁清0是很低效的。

所以用mmap來分配長生命週期的大內存塊就是最好的選擇,其他情況下都不太高效。

4. M_MMAP_MAX
M_MMAP_MAX用於設置進程中用mmap分配的內存塊的最大限制,默認值爲64K(cat /proc/sys/vm/max_map_count),因爲有些系統用mmap分配的內存塊太多會導致系統的性能下降。

如果將M_MMAP_MAX設置爲0,ptmalloc將不會使用mmap分配大塊內存。

Ptmalloc爲優化鎖的競爭開銷,做了PER_THREAD的優化,也提供了兩個選項,M_ARENA_TEST和M_ARENA_MAX,由於PER_THREAD的優化默認沒有開啓,這裏暫不對這兩個選項做介紹。
另外,ptmalloc沒有提供關閉mmap分配閾值動態調整機制的選項,mmap分配閾值動態調整時默認開啓的,如果要關閉mmap分配閾值動態調整機制,可以設置M_TRIM_THRESHOLD,M_MMAP_THRESHOLD,M_TOP_PAD和M_MMAP_MAX中的任意一個。但是強烈建議不要關閉該機制,該機制保證了ptmalloc儘量重用緩存中的空閒內存,不用每次對相對大一些的內存使用系統調用mmap去分配內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章