jemalloc內存分配原理【轉】


原文:http://www.cnblogs.com/gaoxing/p/4253833.html

內存分配是面向虛擬內存的而言的,以頁為單位進行管理的,頁的大小一般為4kb,當在堆里創建一個對象時(小於4kb),會分配一個頁,當再次創建一個對象時會判斷該頁剩余大小是否夠,夠的話使用該頁剩余的內存,減少系統調用。真實的內存分配算法比這個復雜了,效率不好的內存算法會導致出現很多內存碎片。

內存分配的核心思想概括起來有3條

1:首先講內存區(memory pool)以最小單位(chunk)定義出來 ,然后區分對象大小分別管理內存,小內存定義不同的規格(bins),根據不同的bin分配固定大小的內存塊,並用一個表
管理起來,大對象則以頁為單位進行管理,配合小對象所在的頁,降低碎片,設計一個好的存儲方案(metadata)減少對內存的占用,同時優化內存信息的存儲。以使對每個bin或大內存區域的訪問性能最優且有上限。


2:當釋放內存時,要能夠合並小內存為大內存,該保留的保留下次可快速響應,不該保留的釋放給系統
3:多線程環境下,每個線程可以獨立的占有一段內存區間(TLS),這樣線程內操作可以不加鎖

 

jemalloc是freebsd的內存分配算法,他的layout如下:

1:arena:把內存分成許多不同的小塊來分而治之,該小塊便是arena,讓我們想象一下,給幾個小朋友一張大圖紙,讓他們隨意地畫點。結果可想而知,他們肯定相互顧忌對方而不敢肆意地畫(synchronization),從而影響畫圖效率。但是如果老師事先在大圖紙上划分好每個人的區域,小朋友們就可以又快又准地在各自地領域上畫圖。這樣的概念就是arena。它是jemalloc的核心分配管理區域,對於多核系統,會默認分配4*cores個arena 。線程采用輪詢的方式來選擇響應的arena進行內存分配。

2: chunk。具體進行內存分配的區域,默認大小是4M,chunk以page為單位進行管理,每個chunk的前6個page用於存儲后面page的狀態,比如是否分配或已經分配

3:bin:用來管理各個不同大小單元的分配,比如最小的Bin管理的是8字節的分配,每個Bin管理的大小都不一樣,依次遞增。

4:run:每個bin在實際上是通過對它對應的正在運行的Run進行操作來進行分配的,一個run實際上就是chunk里的一塊區域,大小是page的整數倍,具體由實際的bin來決定,比如8字節的bin對應的run就只有1個page,可以從里面選取一個8字節的塊進行分配。在run的最開頭會存儲着這個run的信息,比如還有多少個塊可供分配。

5:tcache。線程對應的私有緩存空間,默認是使用的。因此在分配內存時首先從tcache中找,miss的情況下才會進入一般的分配流程。

arena和bin的關系:每個arena有個bin數組,每個bin管理不同大小的內存(run通過它的配置去獲取相應大小的內存),每個tcahe有一個對應的arena,它本身也有一個bin數組(稱為tbin),前面的部分與arena的bin數組是對應的,但它長度更大一些,因為它會緩存一些更大的塊;而且它也沒有對應的run的概念

chunk與run的關系:chunk默認是4M,而run是在chunk中進行實際分配的操作對象,每次有新的分配請求時一旦tcache無法滿足要求,就要通過run進行操作,如果沒有對應的run存在就要新建一個,哪怕只分配一個塊,比如只申請一個8字節的塊,也會生成一個大小為一個page(默認4K)的run;再申請一個16字節的塊,又會生成一個大小為4096字節的run。run的具體大小由它對應的bin決定,但一定是page的整數倍。因此實際上每個chunk就被分成了一個個的run。

 

內存分配的,具體流程如下:

       1.   如果請求size不大於arena的最小的bin(筆者機器上是3584字節),那么就通過線程對應的tcache來進行分配。首先確定size的大小屬於哪一個tbin,比如2字節的size就屬於最小的8字節的tbin,然后查找tbin中有沒有緩存的空間,如果有就進行分配,沒有則為這個tbin對應的arena的bin分配一個run,然后把這個run里面的部分塊的地址依次賦給tcache的對應的bin的avail數組,相當於緩存了一部分的8字節的塊,最后從這個availl數組中選取一個地址進行分配;
       2.   如果請求size大於arena的最小的bin,同時不大於tcache能緩存的最大塊(筆者機器上是32K),也會通過線程對應的tcache來進行分配,但方式不同。首先看tcache對應的tbin里有沒有緩存塊,如果有就分配,沒有就從chunk里直接找一塊相應的page整數倍大小的空間進行分配(當這塊空間后續釋放時,這會進入相應的tcache對應的tbin里);
       3.   如果請求size大於tcache能緩存的最大塊,同時不大於chunk大小(默認是4M),具體分配和第2類請求相同,區別只是沒有使用tcache;
       4.   如果請求大於chunk大小,直接通過mmap進行分配。
 
       回收流程大體和分配流程類似,有tcache機制的會將回收的塊進行緩存,沒有tcache機制的直接回收(不大於chunk的將對應的page狀態進行修改,回收對應的run;大於chunk的直接munmap)。需要關注的是jemalloc何時會將內存還給操作系統,因為ptmalloc中存在因為使用top_chunk機制(詳見華庭的文章)而使得內存無法還給操作系統的問題。目前看來,除了大內存直接munmap,jemalloc還有兩種機制可以釋放內存:
       1.   當釋放時發現某個chunk的所有內存都已經為臟(即分配后又回收)就把整個chunk釋放;
       2.   當arena中的page分配情況滿足一個閾值時對dirty page進行purge(通過調用madvise來進行)。這個閾值的具體含義是該arena中的dirty page大小已經達到一個chunk的大小且占到了active page的1/opt_lg_dirty_mult(默認為1/32)。active page的意思是已經正在使用中的run的page,而dirty page就是其中已經分配后又回收的page。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM