1.Zend內存池多線程
內存池是內核中最底層的內存操做,定義了三種粒度的內存塊:chunk、page、slot,每一個chunk的大小爲2M,page大小爲4KB,一個chunk被切割爲512個page,而一個或若干個page被切割爲多個slot,因此申請內存時按照不一樣的申請大小決定具體的分配策略:
Huge(chunk): 申請內存大於2M,直接調用系統分配,分配若干個chunk Large(page): 申請內存大於3K(3/4 page_size),小於2044K(511 page_size),分配若干個page Small(slot): 申請內存小於等於3K(3/4 page_size)
2.zend堆結構spa
chunk由512個page組成,其中第一個page用於保存chunk結構,剩下的511個page用於內存分配,page主要用於Large、Small兩種內存的分配;heap是表示內存池的一個結構,它是最主要的一個結構,用於管理上面三種內存的分配,Zend中只有一個heap結構。但在多線程模式下(ZTS)會有多個heap,也就是說每一個線程都有一個獨立的內存池
3.內存分配線程
Huge分配code
超過2M內存的申請,與通用的內存申請沒有太大差異,只是將申請的內存塊經過單鏈表進行了管理。huge的分配實際就是分配多個chunk,chunk的分配也是large、small內存分配的基礎,它是ZendMM向系統申請內存的惟一粒度。在申請chunk內存時有一個關鍵操做,那就是將內存地址對齊到ZEND_MM_CHUNK_SIZE,也就是說申請的chunk地址都是ZEND_MM_CHUNK_SIZE的整數倍
Large分配圖片
大於3/4的page_size(4KB)且小於等於511個page_size的內存申請,也就是一個chunk的大小夠用(之因此是511個page而不是512個是由於第一個page始終被chunk結構佔用),若是申請多個page的話分配的時候這些page都是連續的 。若是直到最後一個chunk也沒找到則從新分配一個新的chunk並插入chunk鏈表,
chunk->free_map利用bitmap來記錄每組的page的使用狀況
a.首先會直接跳過group1,直接到group2檢索 b.在group2中找到第一個可用page位置:67,而後向下找第一個不可用page位置:69,找到的可用內存塊長度爲2,小於3,表示此內存塊不可用 c.接着再次在group2中查找到第一個可用page位置:71,而後向下找到第一個不可用page位置:75,內存塊長度爲4,大於3,表示找到一個符合的位置,雖然已經找到可用內存塊但並不"完美",先將這個並不完美的page_num及len保存到best、best_len,若是後面沒有比它更完美的就用它了 d.再次檢索,發現group2已無可用page,進入group3,找到可用內存位置:page 130-132,大小比c中找到的合適,因此最終返回的page就是130-132 e.page分配完成後會將free_map對應整數的bit位從page_num至(page_num+page_count)置爲1
Small分配內存
small內存指的是小於(3/4 page_size)的內存,這些內存首先也是申請了1個或多個page,而後再將這些page按固定大小切割了,因此第一步與上一節Large分配徹底相同。small內存總共有30種固定大小的規格:8,16,24,32,40,48,56,64,80,96,112,128 ... 1792,2048,2560,3072 Byte,咱們把這稱之爲slot,這些slot的大小是有規律的:最小的slot大小爲8byte,前8個slot依次遞增8byte,後面每隔4個遞增值乘以2
step1: 首先根據申請內存的大小在heap->free_slot中找到對應的slot規格bin_num,若是當前slot爲空則首先分配對 應的page,free_slot[bin_num]始終指向第一個可用的slot step2: 若是申請內存大小對應的的slot鏈表不爲空則直接返回free_slot[bin_num],而後將free_slot[bin_num]指向 下一個空閒位置 step3: 釋放內存時先將此內存的next_free_slot指向free_slot[bin_num],而後將free_slot[bin_num]指向釋放的內\存,也就是將釋放的內存插到鏈表頭部