最近正在學習linux下堆的管理機制,收集了書籍和網絡上的資料,以自己的理解做了整理,做個記錄。如果有什么不對的地方歡迎指出!
Memory Allocator
常見的內存管理機制
- dlmalloc:通用分配器
- ptmalloc2:glibc分配器,繼承自dlmalloc,並提供了多線程支持,主要研究對象。
- jemalloc:Firefox
- tcmalloc:Chrome
- 其他:編程語言內存分配及回收,比如python
- ......
malloc工作機制
第一次調用malloc
內存分配機制
頭文件:#include<unistd.h>
- brk()
- 函數原型:int brk(void* end_data_segment)
- 功能和作用:用於設置program_break指向的位置。
- sbrk()
- 函數原型:void* sbrk(intptr_t increment)
- 功能和作用:同brk(),參數可以是負數。執行成功返回上一次program_break的值,可以設置參數為0返回當前的program_break.
- mmap()
- 功能和作用:當用戶申請空間大於等於128kb,也就是0x20000字節時,不再使用brk()進行分配,改為使用mmap()。
- unmmap()
- 功能和作用:堆mmap()申請的空間進行回收。
內存分配圖
- 主線程的arena就是main_arena,包含start_brk和brk中間的連續內存,當main_arena不夠分配時,會使用brk()進行擴展。
- 子線程arena可以有多片連續內存,但是大小是固定的,不可以擴展,如果不夠用的話需要再次調用mmap()來分配。
第二次調用malloc
- 只要分配的空間不超過128kb,則不會再次向system申請空間,超過時才會調用brk()進行擴展。
- 即使將main_arena全部free,也不會立即把內存還給操作系統,此時內存由glib進行管理。
chunk
chunk是glibc管理內存的基本單元。主要分為以下幾類:
- alloced chunk:已分配正在使用中的chunk。
- free chunk:已經free的chunk。
- top chunk:可以理解為地址的最高處,還沒有分配的chunk。
- last remainder chunk:是為了提高內存分配的局部性。
chunk = chunk header + user data,malloc返回給用戶的其實是user data指針,具體如下圖:
alloced chunk結構
- size:本chunk的大小,包括prev,大小為8的整數倍。32位以8字節對齊,最小為0x10。64位以16字節對齊,最小為0x20。其中低三位有特殊含義,分別為N、M、P
- N位:是否屬於主進程。
- M位:是否由mmap()分配。
- P位:前一堆塊占用標志,1為占用,0為空閑。
- 當P位為0時,表示前一堆塊釋放,prev表示前一堆塊的大小。當P位為1,表示前一堆塊使用,prev表示前一堆塊的數據。
- userdata為輸入的數據。
- 將下一堆塊的P位設置為1。
free chunk
- 其中fd、bk屬於鏈表指針,有特殊用途,后面會講到。
- prev_size為當前釋放塊的大小(包含chunk header)
- 下一堆塊P位通常被設置為0(fastbin除外)。
top chcunk
- 該堆塊位於前兩種堆塊之后,頭部結構與alloced相似
- size:top chunk還有多少空間可以分配。
- 重要的是P位:0表示上一堆塊處於空閑,1表示上一堆塊處於使用狀態。主要用於判斷free時是否能與上一堆塊進行合並(fastbin除外)。
last remainder chunk
- 在malloc時,如果有比較大的chunk可以分配,會把這個chunk分成兩部分,一部分返回給用戶,另一部分稱為remainder,加入到 unsorted bin,last remainder會記錄最近拆分的remainder。這個remainder大小至少要為MINSIZE,否則不能拆分。
- 當下次malloc時,如果last remainder chunk夠大,則重復上一過程。
- 拆分的情況:fast bin 和 small bin 都沒有合適的chunk,同時unsorted bin有且只有一個可拆分的chunk,並且這個chunk 是last remainder。
堆空閑塊管理結構bin
當alloced chunk被釋放后,會根據大小放入bin或者合並到top chunk 中去。bin的主要作用時加快分配速度,通過鏈表方式(chunk中的fd和bk指針)進行管理。主要有以下幾種,顧名思義:
- fast bin
- unsorted bin
- small bin
- large bin
fastbinsY:這是一個bin數組,里面有NFASTBINS個fast bin
bins:也是一個bin數組,一共有126個bin,按順序分別是:
- bin 1 為unsorted bin
- bin 2 到 bin 63 為small bin
- bin 64 到 bin 126 為 large bin
fast bin
- 這類bin通常申請和釋放的堆塊都比較小,所以使用單鏈表結構,LIFO(后進先出)分配策略。
- 為了速度,fast bin不會進行合並,下一個chunk始終處於使用狀態。
- 在fastbinsY數組里按照從小到大的順序排列。
- 以64位為例,fast bin結構如下(大小區間0x200x80,32位為0x100x40):
unsorted bin
- 一定大小堆塊被釋放時,在加入small bin 和large bin 之前,會首先加入此bin,可以加快分配速度。使用雙鏈表結構,FIFO(先進先出)分配策略。
- unsorted bin大小可能是不相同的。
- 由於使用雙鏈表,一個bin會占用bins的兩個元素。fd指向上一個chunk,bk指向下一個。
- 以64位為例,unsorted bin結構如下(非連續內存,大小無限制):
small bin
- 同一個small bin里的chunk大小相同,使用雙鏈表結構,FIFO(先進先出)分配策略。
- 由於fast bin和small bin 有重合部分,在某些情況下會加入到small bin
- 根據大小分成62個不同的bin,0x20,0x30,0x40...0x80,0x90...1008
- 以64位為例,small bin結構如下(大小區間:size<0x400byte):
large bin
- 使用雙鏈表結構,FIFO(先進先出)分配策略。
- free時bk后面多兩個此參數:fd_nextsize、bk_nextsize。分別指向前一個和后一個large chunk。
- 根據大小分成63個不同的bin,大小不再固定。前32個bin為 0x400+64i,32-48 bin為 0x1380+512j,依此類推。並且會將大的chunk放在前面,小的放在后面,以加快速度。
- 以64位為例,large bin大小區間:size>=1024byte。32位為:size>=512byte。
- fd_nextsize和bk_nextsize指針用於指向第一個與自己大小不同的chunk,所以也只有在加入了大小不同的chunk時,這兩個指針才會被修改。
隨后附上glibc內存管理流程圖
看不清楚可以保存下來放大。