php 內存分配

    php內核中的內存分配 使用的函數有 emalloc(), erealloc() ,這兩個函數分別是malloc(),realloc()函數的封裝php

關於內存分配有四個容器:cache,小塊內存鏈表,大塊內存(鏈表+樹), 剩餘rest內存鏈表html

大塊內存(鏈表+二叉排序樹):鏈表中每一個bucket除了有前繼結點,後繼結點外,仍是二叉樹的root結點, 通過內存對齊後, 可分配內存的大小是8的倍數git

例如 40的 二進制是 101000,位於大塊內存中鏈表的第5個bucket(從右往左數,最高位是1, 處於第5位了)github

假設如今第5個bucker沒有數據,天然101000放進去,算法

而後插入56,2進製爲 111000數組

32位機來講, 111000前面還有26個0,先左移32-5=27,爲何減5,由於處於某個bucket的最高位所在的位數是同樣,但後面不同,這裏要比較後面的)緩存

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0

左移就是作乘法,若是移後的位數超過32位,則去掉最高位cookie

先左移26位app

1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

再左移1位,把第一個1捨棄,若是不捨棄,最右邊補0的話,就變成33位了,因此直接捨棄第一個1dom

1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

而後再右移 32-1位,變成

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

 

這麼折騰,其實就是爲了拿到111000中的第2個數字 1, 由於是1,因此查看40的右子樹(若是是0,則看左子樹), 由於此時40的右子樹爲空,因此56直接插進去

 

接着插入48,其2進制是110000, 其32位中,最左邊有26個0, 一樣須要左移27位

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0

先左移26位

1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

再左移1位

1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 

這裏再右移31位,移後爲

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

40的右子樹裏已經有數據 56了, 那麼48再左移1位, 再以56爲基準點,看其左,右子樹是否有數據

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

再右移32位,仍是所有爲0,再跟1作&運算,發現是0,又由於56的左子樹沒有數據,因此將48作爲56的左子樹

其實從48的插入看出

先左移32-5=27位,再右移32-1=31位,最後等於右移了4位 //獲得110000第2個數字1(從左往右數)

而後再左移一位,也就是移動 28位,再右移31位,最後等於右移了 31-28=3位 //獲得110000第3個數字0 (從左往右數)

這四個窗器只保存所分配內存的大小,以及地址

zend_mm_seg_size這個環境變量默認爲256K,當內核使用emalloc()申請內存時,php會調用malloc()申請256K大小的內存(256K內存使用mmap來分配,好處是,回收時直接給OS,不會被ptmalloc緩存)

將內核申請的內存返回給它後,剩餘的內存根據狀況 放入 上面的四個容器中

假設如今申請9字節內存,那麼因爲內存對齊,實際上要分配24字節

若是申請1字節內存,那麼實際會返回16字節

 

php最小分配內存爲272B

這塊寫的不對,當時太不仔細了, 分配1字節的內存,實際上是分配了16字節,除了1個字節存數據之外,還要有4字節存上一段內存的大小,以及本次分配內存的大小,還有4字節的魔法位, 共13字節,以8字節對齊,故分配了16字節

 

<?php
function align($size){
        return ($size + 8 - 1) & (~ (8 - 1) );
}
for($i=0;$i<50;$i++){
        echo $i.' align ' . align($i);
        echo "\n";
}              

 

0 align 0
1 align 8
2 align 8
3 align 8
4 align 8
5 align 8
6 align 8
7 align 8
8 align 8
9 align 16
10 align 16
11 align 16
12 align 16
13 align 16
14 align 16
15 align 16
16 align 16
17 align 24
18 align 24
19 align 24
20 align 24
21 align 24
22 align 24
23 align 24
24 align 24
25 align 32
26 align 32
27 align 32
28 align 32
29 align 32
30 align 32
31 align 32
32 align 32
33 align 40
34 align 40
35 align 40
36 align 40
37 align 40
38 align 40
39 align 40
40 align 40
41 align 48
42 align 48
43 align 48
44 align 48
45 align 48
46 align 48
47 align 48
48 align 48
49 align 56

 

 

 

//指定的大小轉爲真實的大小
#define ZEND_MM_TRUE_SIZE(size) ((size<ZEND_MM_MIN_SIZE)?(ZEND_MM_ALIGNED_MIN_HEADER_SIZE):(ZEND_MM_ALIGNED_SIZE(size+ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE)))

ZEND_MM_ALIGNED_SIZE = (size < 4) ? 16 : ZEND_MM_ALIGNED_SIZE(size+8+4)


#define ZEND_MM_MIN_SIZE                    

( (ZEND_MM_ALIGNED_MIN_HEADER_SIZE>(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE) )

        ?( ZEND_MM_ALIGNED_MIN_HEADER_SIZE-(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE))  :0)

ZEND_MM_MIN_SIZE = (16 > (8 + 4)) ? 16-(8+4): 0 = 4

ZEND_MM_ALIGNED_MIN_HEADER_SIZE 16    
ZEND_MM_ALIGNED_HEADER_SIZE 8    
ZEND_MM_MIN_ALLOC_BLOCK_SIZE 16
# define END_MAGIC_SIZE sizeof(unsigned int) 4

#define ZEND_MM_ALIGNED_MIN_HEADER_SIZE        (ZEND_MM_MIN_ALLOC_BLOCK_SIZE>ZEND_MM_ALIGNED_FREE_HEADER_SIZE?ZEND_MM_MIN_ALLOC_BLOCK_SIZE:ZEND_MM_ALIGNED_FREE_HEADER_SIZE) //16
ZEND_MM_ALIGNED_MIN_HEADER_SIZE = (16 > 16) ? 16 : 16 = 16

#define ZEND_MM_MIN_ALLOC_BLOCK_SIZE
    ZEND_MM_ALIGNED_SIZE(ZEND_MM_ALIGNED_HEADER_SIZE + END_MAGIC_SIZE)
ZEND_MM_MIN_ALLOC_BLOCK_SIZE = ZEND_MM_ALIGNED_SIZE(8 + 4) = 16

#define ZEND_MM_ALIGNED_HEADER_SIZE  ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block)) 
ZEND_MM_ALIGNED_HEADER_SIZE = ZEND_MM_ALIGNED_SIZE(8) = 8

#define ZEND_MM_ALIGNED_FREE_HEADER_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block))
ZEND_MM_ALIGNED_FREE_HEADER_SIZE = ZEND_MM_ALIGNED_SIZE(16) = 16


/**
 *傳入的大小內存以8字節進行對齊
 *爲何是8字節  外部總線從內存一次獲取的數據每每不是1byte,而是4bytes或許8bytes,或者更多~~
 *詳見https://www.zhihu.com/question/23791224
 *公式 (size + 8 - 1) & ~(8 - 1)
 *即 (size + 7) & ~7
 */
 #define ZEND_MM_ALIGNED_SIZE(size)    (((size) + ZEND_MM_ALIGNMENT - 1) & ZEND_MM_ALIGNMENT_MASK)

#define ZEND_MM_ALIGNMENT 8
#define ZEND_MM_ALIGNMENT_MASK ~(ZEND_MM_ALIGNMENT-1) 

 

 

php的內存分配中,使用了位圖法

例如 sizt_t a;
在32位機下:變量a佔用4個字節,共32位

在64位機下:變量a佔用8個字節,共64位

                                                               

每一位 可認爲是一小格,在32位機下,共32格

如今把 數字4放到第4個格里去,

下標從0開始  4%32=4

                                                      1        

 

也即 a |= (1<<4)

由於 a中全爲0, 2 的4次方爲16,0|16作運算,必然第4位爲1,(下標從0開始)

 

1.幾個結構體

typedef struct _zend_mm_block_info { 
int _size;  
int _prev;  
} zend_mm_block_info; 

typedef struct _zend_mm_block { 
zend_mm_block_info info; 
} zend_mm_block; 

typedef struct _zend_mm_small_free_block { 
zend_mm_block_info info; 
struct _zend_mm_free_block *prev_free_block; 
struct _zend_mm_free_block *next_free_block; 
} zend_mm_small_free_block; 

typedef struct _zend_mm_free_block { 
zend_mm_block_info info; 
struct _zend_mm_free_block *prev_free_block; 
struct _zend_mm_free_block *next_free_block; 
struct _zend_mm_free_block **parent; 
struct _zend_mm_free_block *child[2]; 
} zend_mm_free_block; 

struct _zend_mm_heap {
    int                 use_zend_alloc;
    void               *(*_malloc)(size_t);
    void                (*_free)(void*);
    void               *(*_realloc)(void*, size_t);
    size_t              free_bitmap;
    size_t              large_free_bitmap;
    size_t              block_size;
    size_t              compact_size;
    zend_mm_segment    *segments_list;
    zend_mm_storage    *storage;
    size_t              real_size;
    size_t              real_peak;
    size_t              limit;
    size_t              size;
    size_t              peak;
    size_t              reserve_size;
    void               *reserve;
    int                 overflow;
    int                 internal;
#if ZEND_MM_CACHE
    unsigned int        cached;
    zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS];
#endif
    zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];
    zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];
    zend_mm_free_block *rest_buckets[2];
    int                 rest_count;
#if ZEND_MM_CACHE_STAT
    struct {
        int count;
        int max_count;
        int hit;
        int miss;
    } cache_stat[ZEND_MM_NUM_BUCKETS+1];
#endif
};

 

2.

#define ZEND_MM_SMALL_FREE_BUCKET(heap, index) 
(zend_mm_free_block*) ( (char*)&heap->free_buckets[index * 2] + sizeof(zend_mm_free_block*) * 2 - sizeof(zend_mm_small_free_block) )

 

 注意到

sizeof(zend_mm_free_block*)*2=8  一個指針佔4個字節

sizeof(zend_mm_small_free_block)= 16 

二者之差爲4,即爲sizeof(zend_mm_block_info)

假設 ZEND_MM_SMALL_FREE_BUCKET這個宏 返回值賦值給 zend_mm_free_block *p , 那麼p的地址 + sizeof(zend_mm_block_info) 就取出p->pre_free_block了

p的地址+sizeof(zend_mm_block_info)同時也是&heap->free_buckets[index*2]的地址,都是8個字節

p=(char*)&heap->free_buckets[index*2]-sizeof(zend_mm_block_info)

 

3 zend_mm_remove_from_free_list

移除被選中的內存塊函數

static inline void zend_mm_remove_from_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block)
{
//取出mm_block的上一個內存塊和下一個內存塊 zend_mm_free_block
*prev = mm_block->prev_free_block; zend_mm_free_block *next = mm_block->next_free_block; //prev等於mm_block,說明這是某個bucket下面的第一個內存塊 if (EXPECTED(prev == mm_block)) { zend_mm_free_block **rp, **cp;     
/**
*while(*(cp=&(prev->child[prev->child[1]!=NULL]))!=NULL)
   *若是mm_block的右孩子爲空,那麼判斷mm_block的左孩子是否爲空,若是不爲空,以此左孩子爲父結點,再往下尋找
*若是mm_block的右孩子不爲空,就以此右孩子爲父結點,一直向下尋找
*一直找到最後一個結點,賦值另外一個變量p,而後將這個最後結點賦值爲空
*將變量p的parent指向mm_block的parent
*將變量p的child[1]指向mm_block的child[1],將變量p的child[0]指向mm_block的child[0]
*按代碼的思路是這樣,但不知道爲何這樣作
*
*我猜想了下,之因此用最後一個節點替換mm_block,而不用mm_block下面的左,或右孩子,是由於若是左,右孩子同時存在,
*選擇其中一個孩子替換了mm_block,那剩下的那個孩子被放置哪裏呢?

*/
rp
= &mm_block->child[mm_block->child[1] != NULL]; prev = *rp; if (EXPECTED(prev == NULL)) { size_t index = ZEND_MM_LARGE_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block)); ZEND_MM_CHECK_TREE(mm_block); *mm_block->parent = NULL; if (mm_block->parent == &heap->large_free_buckets[index]) { heap->large_free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index); } } else { while (*(cp = &(prev->child[prev->child[1] != NULL])) != NULL) { prev = *cp; rp = cp; } *rp = NULL; subst_block: ZEND_MM_CHECK_TREE(mm_block); *mm_block->parent = prev; prev->parent = mm_block->parent; if ((prev->child[0] = mm_block->child[0])) { ZEND_MM_CHECK_TREE(prev->child[0]); prev->child[0]->parent = &prev->child[0]; } if ((prev->child[1] = mm_block->child[1])) { ZEND_MM_CHECK_TREE(prev->child[1]); prev->child[1]->parent = &prev->child[1]; } } } else {      //若是mm_block沒有前,後內存塊,則直接經過鏈表操做將其跳過 prev->next_free_block = next; next->prev_free_block = prev; if (EXPECTED(ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(mm_block)))) {
//mm_block是小塊內存
if (EXPECTED(prev == next)) {
//這個mm_block是某於某個bucket的第一個內存塊 size_t index
= ZEND_MM_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block));
//heap->free_buckets[]這個指針數組其實有64個元素,每兩個元素一組,分別爲大小相同內存鏈表的頭和尾
if (EXPECTED(heap->free_buckets[index*2] == heap->free_buckets[index*2+1])) {
//設置heap->free_bitmap 位圖,將第index個桶的位 置0,表示這個桶沒有可以使用的內存 heap
->free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index); } } } else if (UNEXPECTED(mm_block->parent == ZEND_MM_REST_BLOCK)) { heap->rest_count--; } else if (UNEXPECTED(mm_block->parent != NULL)) {
//說明mm_block是大塊內存
goto subst_block; } } }

 

4.zend_mm_add_to_free_list

添加內存塊

static inline void zend_mm_add_to_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block)
{
    size_t size;
    size_t index;

    ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_FREED);
    //獲得mm_block的大小
    size = ZEND_MM_FREE_BLOCK_SIZE(mm_block);
    if (EXPECTED(!ZEND_MM_SMALL_SIZE(size))) {
//要添加的mm_block爲大塊內存 zend_mm_free_block
**p; //找到要插入的bucket index = ZEND_MM_LARGE_BUCKET_INDEX(size);

//在大內存列表中,取出頭結點 p
= &heap->large_free_buckets[index]; mm_block->child[0] = mm_block->child[1] = NULL; if (!*p) {
//上面的頭結點 爲空,不存在,設置mm_block的parent,prev_free_block,next_free_block的屬性,再賦值給p,mm_block即成爲頭結點
*p = mm_block; mm_block->parent = p; mm_block->prev_free_block = mm_block->next_free_block = mm_block; heap->large_free_bitmap |= (ZEND_MM_LONG_CONST(1) << index); } else {
size_t m;        

       /**
*構造一顆二叉排序樹
*左結點小於根結點,右結點大於根結點
       *具體算法:
*插入6,成爲頭結點
*再插入4
       *4 二進制 28個0 0100
       *4 的最高位是第二位
       *4再左移32-2次,即左移30次,那麼第一位(從最左數)爲0,則成爲6的左子結點
*插入5
*5二進制 28個0 0101
*5的最高位是 第二們
*5再左移32-2=30次,那第一位爲0,但已被4佔用,因此 5左移30次後再左移1次,最高位爲1,成爲4的右子結點
*/
for (m = size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) { zend_mm_free_block *prev = *p; if (ZEND_MM_FREE_BLOCK_SIZE(prev) != size) {
p
= &prev->child[(m >> (ZEND_MM_NUM_BUCKETS-1)) & 1]; if (!*p) { *p = mm_block; mm_block->parent = p; mm_block->prev_free_block = mm_block->next_free_block = mm_block; break; } } else {
//大小跟頭結點相等,說明是他的兄弟,這時只須要插入到頭結點的後面便可 zend_mm_free_block
*next = prev->next_free_block; prev->next_free_block = next->prev_free_block = mm_block; mm_block->next_free_block = next; mm_block->prev_free_block = prev; mm_block->parent = NULL; break; } } } } else {

/**
*對插入的小塊內存進行操做
*a)取出對應的bucket index值
*b)取出該bucket的頭結點
*c)設置位圖,將第index個位設置爲1,說明這個index有空閒內存塊
*d)將要插入的內在塊放在頭結點後面
*/ zend_mm_free_block
*prev, *next; index = ZEND_MM_BUCKET_INDEX(size); prev = ZEND_MM_SMALL_FREE_BUCKET(heap, index); if (prev->prev_free_block == prev) { heap->free_bitmap |= (ZEND_MM_LONG_CONST(1) << index); } next = prev->next_free_block; mm_block->prev_free_block = prev; mm_block->next_free_block = next; prev->next_free_block = next->prev_free_block = mm_block; } }

 

5.zend_mm_alloc_int 分配內存

 

 

static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
    zend_mm_free_block *best_fit;
    size_t true_size = ZEND_MM_TRUE_SIZE(size);
    /*
        獲取真實內存,內存須要對齊 (實際申請內存+8字節大小的頭信息+4字節的魔術結束信息) 16字節對齊 
        假設只申請1字節,那麼最終申請的是1+8+4=13字節的內存
    */

    size_t block_size;
    size_t remaining_size;
    size_t segment_size;
    zend_mm_segment *segment;
    int keep_rest = 0;

    if (EXPECTED(ZEND_MM_SMALL_SIZE(true_size))) {
        /*
            須要分配的內存爲小內存,計算出該內存在free_buckets中的下標
            計算方法
            #define ZEND_MM_BUCKET_INDEX(true_size)     ((true_size>>ZEND_MM_ALIGNMENT_LOG2)-   (ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2))
            =》ZEND_MM_BUCKET_INDEX(16)
                =》 16>> 3 - 16>>3 = 0 即16字節在下標爲0的地方
            注: # define ZEND_MM_ALIGNMENT_LOG2 3
         */
        size_t index = ZEND_MM_BUCKET_INDEX(true_size);
        size_t bitmap;

        if (UNEXPECTED(true_size < size)) {
            goto out_of_memory;
        }
#if ZEND_MM_CACHE
        if (EXPECTED(heap->cache[index] != NULL)) {
            /* Get block from cache */
#if ZEND_MM_CACHE_STAT
            heap->cache_stat[index].count--;
            heap->cache_stat[index].hit++;
#endif
   
           /**
            *若是設置了cache,從cache中直接返回,並設置cache[index]中的頭結點
            *這個設置頭結點挺怪異,其餘的都是指向下一個結點便可,這個是指向上一個結點
            *將老頭結點+8字節,直接返回,由於老的頭結點包括了8個字節,其中4個字節存儲當前內存塊的大小,另外4字節存儲上一個內存塊的大小
            */

            best_fit = heap->cache[index];
            heap->cache[index] = best_fit->prev_free_block;
            heap->cached -= true_size;
            ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED);
            ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0);
            HANDLE_UNBLOCK_INTERRUPTIONS();
            return ZEND_MM_DATA_OF(best_fit);
         }
#if ZEND_MM_CACHE_STAT
        heap->cache_stat[index].miss++;
#endif
#endif
        
       /**
        *若是cache中沒有找到,接着在小塊內存中查找
        *根據位圖,將heap->free_bitmap 右移index次
        *若是不爲0,說明有空閒內存
        *若是爲0,說明沒有空閒內存,到大塊內存中查找
        *在小塊內存找到後,取出頭結點,該結點的size可能要大於實際申請的大小,
        *若是二者的差 小於 8字節(php內存對齊最小字節),就把該結點的size直接返回給申請者,由於小於8字節的內存不欨分
        *若是二者的差 大於 8字節,調用zend_mm_add_to_free_list函數,插入相應位置
        */
        bitmap = heap->free_bitmap >> index;
        if (bitmap) {
            /* Found some "small" free block that can be used */
          //若是bitmap 爲 10100 即十進制的20 ,那麼 zend_mm_low_bit爲 2, 10100中從右住左數,第1個1,下標爲2,不知道這裏爲何要加這個數?
            index += zend_mm_low_bit(bitmap);
            best_fit = heap->free_buckets[index*2];
#if ZEND_MM_CACHE_STAT
            heap->cache_stat[ZEND_MM_NUM_BUCKETS].hit++;
#endif
            goto zend_mm_finished_searching_for_block;
        }
    }

#if ZEND_MM_CACHE_STAT
    heap->cache_stat[ZEND_MM_NUM_BUCKETS].miss++;
#endif
    
    //在大塊內存中查找
    best_fit = zend_mm_search_large_block(heap, true_size);
    
   //若是cache,小塊內存,大塊內存都沒有找到,就到剩餘內存裏查找
    if (!best_fit && heap->real_size >= heap->limit - heap->block_size) {
        zend_mm_free_block *p = heap->rest_buckets[0];
        size_t best_size = -1;

        while (p != ZEND_MM_REST_BUCKET(heap)) {
            if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) {
                best_fit = p;
                goto zend_mm_finished_searching_for_block;
            } else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size &&
                       ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) {
                best_size = ZEND_MM_FREE_BLOCK_SIZE(p);
                best_fit = p;
            }
            p = p->prev_free_block;
        }
    }
    
   /**
    * 若是剩餘內存裏也沒有找到,就要真實分配內存了
    * 若是申請內存大小 超過256K,通過計算,從OS申請一大塊內存,大小爲256的最小倍數
    * 若是申請內存大小 沒有超過256K,就從OS申請256K內存
    * 根據從OS申請的內存與實際申請內存大小的差,調用zend_mm_add_to_free_list函數,插入相應位置
    */
    if (!best_fit) {
        if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) {
            /* Make sure we add a memory block which is big enough,
               segment must have header "size" and trailer "guard" block */
            segment_size = true_size + ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE;
            segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1);
            keep_rest = 1;
        } else {
            segment_size = heap->block_size;
        }

        if (segment_size < true_size ||
            heap->real_size + segment_size > heap->limit) {
            /* Memory limit overflow */
#if ZEND_MM_CACHE
            zend_mm_free_cache(heap);
#endif
            HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
            zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %lu bytes)", heap->limit, __zend_filename, __zend_lineno, size);
#else
            zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %lu bytes)", heap->limit, size);
#endif
        }

        /*
            # define ZEND_MM_STORAGE_ALLOC(size)    heap->storage->handlers->_alloc(heap->storage, size) 分配一個大小爲256K的內存,稱爲段

         */
        segment = (zend_mm_segment *) ZEND_MM_STORAGE_ALLOC(segment_size);

        if (!segment) {
            return NULL;
        }

        heap->real_size += segment_size;
        if (heap->real_size > heap->real_peak) {
            heap->real_peak = heap->real_size;
        }

        segment->size = segment_size;
        /*
            typedef struct _zend_mm_segment {
                size_t  size;
                struct _zend_mm_segment *next_segment;
            } zend_mm_segment;
            每次新段作爲heap中的segment的鏈頭
        */
        segment->next_segment = heap->segments_list;
        heap->segments_list = segment;

     
        best_fit = (zend_mm_free_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
        /*
            這裏segment就是個內存地址,強制轉爲char*類型,而後再加上ZEND_MM_ALIGNED_SEGMENT_SIZE 爲16字節
            貌似加上後也不能達到對齊的效果?
         */
            

     ZEND_MM_MARK_FIRST_BLOCK(best_fit);
        /*
            #define ZEND_MM_MARK_FIRST_BLOCK(b) ((b)->info._prev = ZEND_MM_GUARD_BLOCK)
            此時的best_fit已經強制轉爲zend_mm_free_block類型,從segment_ZEND_MM_ALIGNED_SEGMENT_SIZE開始的內存被看做爲zend_mm_free_block類型
            以b開始的內存,第1個4字節的內存,保存#define ZEND_MM_GUARD_BLOCK ZEND_MM_LONG_CONST(0x3),表示處於保護段
            假設segment是以0x100開始的,因爲段對齊,因此真正開始的地址爲0x116
            0x116至0x11a這4個字節(_prev)的內容爲3,爲保護狀態,能夠理解爲前面16字節處於保護狀態
     */

    
        block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE;
        /*
            咱們是不能徹底使用這256K大小的內存的,
            除了前面的段對齊,佔16字節, 還有16字節的頭信息,真正的內存大小爲 segment_size-段對齊(16字節)-頭對齊(16字節)
            即256-16-16=224(爲計算方便,這裏就稱256爲256字節,而不是256k了)
            #define ZEND_MM_ALIGNED_HEADER_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block))
        */
 
        ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(best_fit, block_size));

        /*
            可以使用的224字節的開始地址爲0x116,結束地址爲0x33a
            那麼0x33a至0x33d這4個字節的內容置爲3,表示0x33a後面的數據受保護的
            #define ZEND_MM_LAST_BLOCK(b) do { \
                (b)->info._size = ZEND_MM_GUARD_BLOCK | ZEND_MM_ALIGNED_HEADER_SIZE; \
                ZEND_MM_SET_MAGIC(b, MEM_BLOCK_GUARD); \
            } while (0);

            #define ZEND_MM_BLOCK_AT(blk, offset)   ((zend_mm_block *) (((char *) (blk))+(offset)))

            

            這裏會認爲 0x33a後面沒有內存了,其實仍是有內存的
            假設不考慮對齊,malloc分配的內存以0x100開始,分配256字節,那麼結束地址爲0x356
            內存對齊有兩次:malloc以後算一次,即0x100+16=0x116
                第二次:分配256字節,256-段對齊(16)-內存對齊(16) = 224字節
            故結束地址爲0x116+224=0x33a, 0x33a至0x33d這4個字節裏置保護位,即表示0x33d到0x356不容許使用

        */

    } else {
zend_mm_finished_searching_for_block:
        /* remove from free list */
        ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_FREED);
        ZEND_MM_CHECK_COOKIE(best_fit);
        ZEND_MM_CHECK_BLOCK_LINKAGE(best_fit);
        zend_mm_remove_from_free_list(heap, best_fit);

        block_size = ZEND_MM_FREE_BLOCK_SIZE(best_fit);
    }

    /*
       剩餘的內存大小,假設true_size爲16字節,那麼還餘下224-16=208字節的內存
     */
    remaining_size = block_size - true_size;

    if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
        true_size = block_size;
        ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size);
    } else {
        zend_mm_free_block *new_free_block;

        /* prepare new free block */
        ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size);
        new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(best_fit, true_size);
        ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size);

        /*
            #define ZEND_MM_BLOCK(b, type, size)    do { \
                size_t _size = (size); \
                (b)->info._size = (type) | _size; \
                ZEND_MM_BLOCK_AT(b, _size)->info._prev = (type) | _size; \
                ZEND_MM_SET_COOKIE(b); \
            } while (0);

            #define ZEND_MM_FREE_BLOCK      ZEND_MM_LONG_CONST(0x0)     未使用
            #define ZEND_MM_USED_BLOCK       ZEND_MM_LONG_CONST(0x1)    已使用
            #define ZEND_MM_GUARD_BLOCK      ZEND_MM_LONG_CONST(0x3)    受保護

            能夠供咱們使用的開始地址是0x116至0x33a
            以best_fit開始的內存即0x116+4開始的內存地址0x11a(_size)設置爲16|1 (假設須要使用16個字節的內存)
            0x11a到0x11d這四個字節(就是_size)存放 16和1的或運算的結果,表示已使用
            那麼這16字節的結束地址爲0x116+16=0x126
            再以0x126至0x12a這4個字節裏(_prev)設置前面的內存大小爲16|1,表示已使用
            也就意味着,咱們只能使用 0x11d至0x126這8個字節的內存

            那還有剩下的內存 224-16=208字節
            即0x12b至0x12f這4個內存裏 保存剩下的內存大小爲224-16=208字節,即208|0 表示未使用
            剩下內存的結束地址爲0x116+16+(224-16)=0x340,開始地址爲0x116+16=0x126,0x340至 0x344的4個字節裏存208|0 沒有使用過
        */
        /* add the new free block to the free list */
        if (EXPECTED(!keep_rest)) {
            zend_mm_add_to_free_list(heap, new_free_block);
        } else {
            zend_mm_add_to_rest_list(heap, new_free_block);
        }
    }

    ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 1);

    heap->size += true_size;
    if (heap->peak < heap->size) {
        heap->peak = heap->size;
    }

    HANDLE_UNBLOCK_INTERRUPTIONS();

    /*
        #define ZEND_MM_DATA_OF(p)  ((void *) (((char *) (p))+ZEND_MM_ALIGNED_HEADER_SIZE))
        因爲此時的best_fit處的地址並不能被用戶使用,還要加上16即 0x116+8=0x11d,即用戶真正能使用的開始地址爲0x11d至0x126 一共8個字節
     */
    return ZEND_MM_DATA_OF(best_fit);

    //#define ZEND_MM_DATA_OF(p) ((void *) (((char *) (p))+ZEND_MM_ALIGNED_HEADER_SIZE))


}

 

 

 

 

6.zend_mm_search_large_block 尋找大塊內存 

 

根據內存大小size,找到index, 其實就是找到該size的二進制 最高位的下標

對於20,二進制是10100,那麼index爲4,由於最高位 是第4,下標從0開始

下標不會超過31,2的32次方是4G

static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size)
{
    zend_mm_free_block *best_fit;
//取出true_size在大塊內存中的bucket size_t index
= ZEND_MM_LARGE_BUCKET_INDEX(true_size);

   //假設heap->large_free_bitmap 的二進制爲00101100,index爲2,右移2位 0000 1011 最後一位爲1,有空閒內存 size_t bitmap
= heap->large_free_bitmap >> index; zend_mm_free_block *p;
//若是位圖爲0,因此這個bucket裏面沒有空閒內存
if (bitmap == 0) { return NULL; } if (UNEXPECTED((bitmap & 1) != 0)) { /* Search for best "large" free block */ zend_mm_free_block *rst = NULL; size_t m; size_t best_size = -1; best_fit = NULL;

//取出頭結點 p
= heap->large_free_buckets[index]; for (m = true_size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) {
//若是該結點的size正好等於申請的true_size,返回當前結點的下一個內存塊
if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) { return p->next_free_block; } else if (ZEND_MM_FREE_BLOCK_SIZE(p) >= true_size && ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) { best_size = ZEND_MM_FREE_BLOCK_SIZE(p); best_fit = p; }

       /**
*
*/
if ((m & (ZEND_MM_LONG_CONST(1) << (ZEND_MM_NUM_BUCKETS-1))) == 0) {
//移位,若是與m作&運算爲0,說明在左孩子下面,同時右孩子作備胎
if (p->child[1]) { rst = p->child[1]; } if (p->child[0]) { p = p->child[0]; } else { break; } } else if (p->child[1]) {
//移位,若是與m作&運算爲1,說明在右孩子下面 p
= p->child[1]; } else { break; } } for (p = rst; p; p = p->child[p->child[0] != NULL]) { if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) { return p->next_free_block; } else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size && ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) { best_size = ZEND_MM_FREE_BLOCK_SIZE(p); best_fit = p; } } if (best_fit) { return best_fit->next_free_block; } bitmap = bitmap >> 1; if (!bitmap) { return NULL; } index++; }
/**
*這裏的best_fit確定大於true_size,因此遍歷index所在的樹,找到比best_fit小的內存,
*再以best_fit爲基準,找到比它小的內存,依次類推

*/
/* Search for smallest "large" free block */ best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)];
while ((p = p->child[p->child[0] != NULL])) { if (ZEND_MM_FREE_BLOCK_SIZE(p) < ZEND_MM_FREE_BLOCK_SIZE(best_fit)) { best_fit = p; } } return best_fit->next_free_block; }

 

7.zend_mm_realloc_init realloc分配內存

static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{  
//向上減8位,獲得p所在內存塊的首地址 zend_mm_block
*mm_block = ZEND_MM_HEADER_OF(p); zend_mm_block *next_block; size_t true_size; size_t orig_size; void *ptr; #ifdef ZEND_SIGNALS TSRMLS_FETCH(); #endif if (UNEXPECTED(!p) || !ZEND_MM_VALID_PTR(p)) {
//若是p爲空,那麼調用_zend_mm_alloc_init函數
return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } HANDLE_BLOCK_INTERRUPTIONS(); mm_block = ZEND_MM_HEADER_OF(p); true_size = ZEND_MM_TRUE_SIZE(size); orig_size = ZEND_MM_BLOCK_SIZE(mm_block); ZEND_MM_CHECK_PROTECTION(mm_block); if (UNEXPECTED(true_size < size)) { goto out_of_memory; }
/**
*下面的狀況貌似屬於
*emalloc(10);
*erealloc(p,3);
   *這種狀況,即第二次比第一次分配的內存要小
*把第二次分配內存置爲使用狀態,上面的差所在的內存塊設置爲未使用狀態
*/
if (true_size <= orig_size) { size_t remaining_size = orig_size - true_size; if (remaining_size >= ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
zend_mm_free_block
*new_free_block; //利用p所在內存塊的首地址+該內存塊大小(偏移量),計算出下一內存塊的地址,若是沒有使用,則 next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size); if (ZEND_MM_IS_FREE_BLOCK(next_block)) { remaining_size += ZEND_MM_FREE_BLOCK_SIZE(next_block); zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); } /* prepare new free block */

//根據須要申請的內存大小,設置已使用狀態 ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); /* add the new free block to the free list */ zend_mm_add_to_free_list(heap, new_free_block); heap->size += (true_size - orig_size); } ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0); HANDLE_UNBLOCK_INTERRUPTIONS(); return p; }
/**
*從cache中取,並用memcpy進行拷貝,並將p所在內存塊從cache中刪除
*/
#if ZEND_MM_CACHE if (ZEND_MM_SMALL_SIZE(true_size)) { size_t index = ZEND_MM_BUCKET_INDEX(true_size); if (heap->cache[index] != NULL) { zend_mm_free_block *best_fit; zend_mm_free_block **cache; #if ZEND_MM_CACHE_STAT heap->cache_stat[index].count--; heap->cache_stat[index].hit++; #endif best_fit = heap->cache[index]; heap->cache[index] = best_fit->prev_free_block; ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED); ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0); ptr = ZEND_MM_DATA_OF(best_fit); #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION memcpy(ptr, p, mm_block->debug.size); #else memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE); #endif heap->cached -= true_size - orig_size; index = ZEND_MM_BUCKET_INDEX(orig_size); cache = &heap->cache[index]; ((zend_mm_free_block*)mm_block)->prev_free_block = *cache; *cache = (zend_mm_free_block*)mm_block; ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED); #if ZEND_MM_CACHE_STAT if (++heap->cache_stat[index].count > heap->cache_stat[index].max_count) { heap->cache_stat[index].max_count = heap->cache_stat[index].count; } #endif HANDLE_UNBLOCK_INTERRUPTIONS(); return ptr; } } #endif next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size);
/**
*前提條件:p所在的內存塊的下一個內存塊是小內存(p與下一個內存塊是物理排列的),它多是小塊內存,也多是大塊內存
*p所在的內存塊大小與下一塊內存之和大於 所申請內存大小true_size
*把第二塊內存塊從鏈表中去掉
*若是上面的差值大於8,就執行zend_mm_add_free_list()函數

*/
if (ZEND_MM_IS_FREE_BLOCK(next_block)) { ZEND_MM_CHECK_COOKIE(next_block); ZEND_MM_CHECK_BLOCK_LINKAGE(next_block); if (orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block) >= true_size) { size_t block_size = orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block); size_t remaining_size = block_size - true_size; zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { true_size = block_size; ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); } else { zend_mm_free_block *new_free_block; /* prepare new free block */ ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); /* add the new free block to the free list */ if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(new_free_block, remaining_size))) { zend_mm_add_to_rest_list(heap, new_free_block); } else { zend_mm_add_to_free_list(heap, new_free_block); } } ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0); heap->size = heap->size + true_size - orig_size; if (heap->peak < heap->size) { heap->peak = heap->size; } HANDLE_UNBLOCK_INTERRUPTIONS(); return p; } else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(next_block, ZEND_MM_FREE_BLOCK_SIZE(next_block)))) { zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); goto realloc_segment; } } else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(next_block)) { zend_mm_segment *segment; zend_mm_segment *segment_copy; size_t segment_size; size_t block_size; size_t remaining_size; realloc_segment: /* segment size, size of block and size of guard block */ if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) { segment_size = true_size+ZEND_MM_ALIGNED_SEGMENT_SIZE+ZEND_MM_ALIGNED_HEADER_SIZE; segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1); } else { segment_size = heap->block_size; } segment_copy = (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE); if (segment_size < true_size || heap->real_size + segment_size - segment_copy->size > heap->limit) { if (ZEND_MM_IS_FREE_BLOCK(next_block)) { zend_mm_add_to_free_list(heap, (zend_mm_free_block *) next_block); } #if ZEND_MM_CACHE zend_mm_free_cache(heap); #endif HANDLE_UNBLOCK_INTERRUPTIONS(); #if ZEND_DEBUG zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %ld bytes)", heap->limit, __zend_filename, __zend_lineno, size); #else zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %ld bytes)", heap->limit, size); #endif return NULL; } //利用realloc分配一塊新內存 segment = ZEND_MM_STORAGE_REALLOC(segment_copy, segment_size); if (!segment) { #if ZEND_MM_CACHE zend_mm_free_cache(heap); #endif out_of_memory: HANDLE_UNBLOCK_INTERRUPTIONS(); #if ZEND_DEBUG zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %ld bytes)", heap->real_size, __zend_filename, __zend_lineno, size); #else zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %ld bytes)", heap->real_size, size); #endif return NULL; } heap->real_size += segment_size - segment->size; if (heap->real_size > heap->real_peak) { heap->real_peak = heap->real_size; } segment->size = segment_size; if (segment != segment_copy) { zend_mm_segment **seg = &heap->segments_list; while (*seg != segment_copy) { seg = &(*seg)->next_segment; } *seg = segment; mm_block = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); ZEND_MM_MARK_FIRST_BLOCK(mm_block); } block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE; remaining_size = block_size - true_size; /* setup guard block */ ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(mm_block, block_size)); if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { true_size = block_size; ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); } else { zend_mm_free_block *new_free_block; /* prepare new free block */ ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); /* add the new free block to the free list */ zend_mm_add_to_rest_list(heap, new_free_block); } ZEND_MM_SET_DEBUG_INFO(mm_block, size, 1, 1); heap->size = heap->size + true_size - orig_size; if (heap->peak < heap->size) { heap->peak = heap->size; } HANDLE_UNBLOCK_INTERRUPTIONS(); return ZEND_MM_DATA_OF(mm_block); } ptr = _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION memcpy(ptr, p, mm_block->debug.size); #else memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE); #endif _zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); HANDLE_UNBLOCK_INTERRUPTIONS(); return ptr; }

 

內存管理器的啓動

 

# define ZEND_MM_STORAGE_ALLOC(size)                heap->storage->handlers->_alloc(heap->storage, size)

//段對齊
#define ZEND_MM_ALIGNED_SEGMENT_SIZE        ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_segment))

struct _zend_mm_storage {
    const zend_mm_mem_handlers *handlers;
    void *data;
};

/* Heaps with user defined storage */
typedef struct _zend_mm_storage zend_mm_storage;

typedef struct _zend_mm_segment {
    size_t    size;
    struct _zend_mm_segment *next_segment;
} zend_mm_segment;


int zend_startup(zend_utility_functions *utility_functions, char **extensions TSRMLS_DC) /* {{{ */
{

    extern zend_ini_scanner_globals ini_scanner_globals;
    extern zend_php_scanner_globals language_scanner_globals;
    //啓動內存管理器
    start_memory_manager(TSRMLS_C);
    ...
}
ZEND_API void start_memory_manager(TSRMLS_D)
{
    alloc_globals_ctor(&alloc_globals);
}

/*
    USE_ZEND_ALLOC 0 不使用zend包裝的分配內存方法,直接使用C的malloc(這裏沒有緩存)
 */
static void alloc_globals_ctor(zend_alloc_globals *alloc_globals TSRMLS_DC)
{
    char *tmp = getenv("USE_ZEND_ALLOC");

    if (tmp && !zend_atoi(tmp, 0)) {
        alloc_globals->mm_heap = malloc(sizeof(struct _zend_mm_heap));
        memset(alloc_globals->mm_heap, 0, sizeof(struct _zend_mm_heap));
        alloc_globals->mm_heap->use_zend_alloc = 0;
        alloc_globals->mm_heap->_malloc = malloc;
        alloc_globals->mm_heap->_free = free;
        alloc_globals->mm_heap->_realloc = realloc;
    } else {
        alloc_globals->mm_heap = zend_mm_startup();
    }
}

/*
    The Zend MM can be tweaked using ZEND_MM_MEM_TYPE and ZEND_MM_SEG_SIZE environment
    variables.  Default values are "malloc" and "256K". Dependent on target system you
    can also use "mmap_anon", "mmap_zero" and "win32" storage managers.

    //結構體中的方法,模擬類 這裏爲定義
    typedef struct _zend_mm_mem_handlers {
        const char *name;
        zend_mm_storage* (*init)(void *params);
        void (*dtor)(zend_mm_storage *storage);
        void (*compact)(zend_mm_storage *storage);
        zend_mm_segment* (*_alloc)(zend_mm_storage *storage, size_t size);
        zend_mm_segment* (*_realloc)(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size);
        void (*_free)(zend_mm_storage *storage, zend_mm_segment *ptr);
    } zend_mm_mem_handlers;

    //賦值
    static const zend_mm_mem_handlers mem_handlers[] = {
        #ifdef HAVE_MEM_WIN32
            ZEND_MM_MEM_WIN32_DSC,
        #endif
        #ifdef HAVE_MEM_MALLOC
            ZEND_MM_MEM_MALLOC_DSC,
        #endif
        #ifdef HAVE_MEM_MMAP_ANON
            ZEND_MM_MEM_MMAP_ANON_DSC,
        #endif
        #ifdef HAVE_MEM_MMAP_ZERO
            ZEND_MM_MEM_MMAP_ZERO_DSC,
        #endif
            {NULL, NULL, NULL, NULL, NULL, NULL}
    };

    # define ZEND_MM_MEM_MALLOC_DSC {"malloc", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_malloc_alloc, zend_mm_mem_malloc_realloc, zend_mm_mem_malloc_free}
*/
ZEND_API zend_mm_heap *zend_mm_startup(void)
{
    int i;
    size_t seg_size;
    char *mem_type = getenv("ZEND_MM_MEM_TYPE");
    char *tmp;
    const zend_mm_mem_handlers *handlers;
    zend_mm_heap *heap;

    if (mem_type == NULL) {
        i = 0;
    } else {
        ...
    }
    //這個mem_handlers是全局數組,返回zend_mm_mem_handler類型的結構體, 默認爲ZEND_MM_MEM_MALLOC_DSC
    handlers = &mem_handlers[i];

    tmp = getenv("ZEND_MM_SEG_SIZE");
    if (tmp) {
        seg_size = zend_atoi(tmp, 0);
        ... 
        //使用指定的分配內存大小
    } else {
        //#define ZEND_MM_SEG_SIZE   (256 * 1024) 默認256K
        seg_size = ZEND_MM_SEG_SIZE;
    }

    //初始化heap
    heap = zend_mm_startup_ex(handlers, seg_size, ZEND_MM_RESERVE_SIZE, 0, NULL);
    if (heap) {
        ...
    }
    return heap;
}

/* Notes:
 * - This function may alter the block_sizes values to match platform alignment
 * - This function does *not* perform sanity checks on the arguments
     

 */
ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_mem_handlers *handlers, size_t block_size, size_t reserve_size, int internal, void *params)
{
    zend_mm_storage *storage;
    zend_mm_heap    *heap;


#if ZEND_MM_HEAP_PROTECTION
    if (_mem_block_start_magic == 0) {
        zend_mm_random((unsigned char*)&_mem_block_start_magic, sizeof(_mem_block_start_magic));
    }
    if (_mem_block_end_magic == 0) {
        zend_mm_random((unsigned char*)&_mem_block_end_magic, sizeof(_mem_block_end_magic));
    }
#endif
#if ZEND_MM_COOKIES
    if (_zend_mm_cookie == 0) {
        zend_mm_random((unsigned char*)&_zend_mm_cookie, sizeof(_zend_mm_cookie));
    }
#endif

    if (zend_mm_low_bit(block_size) != zend_mm_high_bit(block_size)) {
        fprintf(stderr, "'block_size' must be a power of two\n");
        exit(255);
    }
    /*
        static zend_mm_storage* zend_mm_mem_dummy_init(void *params){
            return malloc(sizeof(zend_mm_storage));
        }
    */
    storage = handlers->init(params);
    if (!storage) {
        fprintf(stderr, "Cannot initialize zend_mm storage [%s]\n", handlers->name);
        exit(255);
    }
    storage->handlers = handlers;
    //分配一塊合適的內存給heap,這裏面已經給free_buckets和large_free_buckets這兩個數組裏分配了內存
    heap = malloc(sizeof(struct _zend_mm_heap));
    if (heap == NULL) {
        fprintf(stderr, "Cannot allocate heap for zend_mm storage [%s]\n", handlers->name);
        exit(255);
    }
    heap->storage = storage;
    heap->block_size = block_size;
    heap->compact_size = 0;
    heap->segments_list = NULL;
    zend_mm_init(heap);
# if ZEND_MM_CACHE_STAT
    memset(heap->cache_stat, 0, sizeof(heap->cache_stat));
# endif

    heap->use_zend_alloc = 1;
    heap->real_size = 0;
    heap->overflow = 0;
    heap->real_peak = 0;
    heap->limit = ZEND_MM_LONG_CONST(1)<<(ZEND_MM_NUM_BUCKETS-2);
    heap->size = 0;
    heap->peak = 0;
    heap->internal = internal;
    heap->reserve = NULL;
    heap->reserve_size = reserve_size;
    if (reserve_size > 0) {
        heap->reserve = _zend_mm_alloc_int(heap, reserve_size ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
    }
    if (internal) {
        //沒有用上
    }
    return heap;
}

//初始化mm,主要是針對小塊內存
static inline void zend_mm_init(zend_mm_heap *heap)
{
    zend_mm_free_block* p;
    int i;

    heap->free_bitmap = 0;
    heap->large_free_bitmap = 0;
#if ZEND_MM_CACHE
    heap->cached = 0;
    memset(heap->cache, 0, sizeof(heap->cache));
#endif
#if ZEND_MM_CACHE_STAT
    for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
        heap->cache_stat[i].count = 0;
    }
#endif
    /*
        #define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \
            (zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \
            sizeof(zend_mm_free_block*) * 2 - \
            sizeof(zend_mm_small_free_block))

        #define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3) 小塊內存有32*2個bucket

        這裏要初始化這32個次,由於64個bucket中每兩個爲一組,prev和next, 這裏只使用到了next
     */
    p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0);
    for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
        p->next_free_block = p;
        p->prev_free_block = p;
        p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2);
        heap->large_free_buckets[i] = NULL;
    }
    heap->rest_buckets[0] = heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(heap);
    heap->rest_count = 0;
}




static zend_mm_segment* zend_mm_mem_malloc_alloc(zend_mm_storage *storage, size_t size)
{
    return (zend_mm_segment*)malloc(size);
}

 

 

 

參考文章

 http://www.codesky.net/article/201011/179466.html

http://www.phppan.com/2010/11/php-source-code-30-memory-pool-storage/

 

http://www.cnblogs.com/mo-beifeng/archive/2011/10/08/2201685.html

 

http://www.phppan.com/2010//php-source-code-32-memory-pool-emalloc-efree/

 

http://blog.chinaunix.net/uid-21586638-id-3822653.html

 

http://www.iamcreater.com/apps/views/techDetail.php?id=147

 

http://www.jb51.net/article/39215.htm

 

http://weibo.com/p/1005051877420547/myfollow?t=1&cfs=&Pl_Official_RelationMyfollow__107_page=2#Pl_Official_RelationMyfollow__107

 

http://easyrss.sturgeon.mopaas.com/index.php?id=2

 

http://www.php-internals.com/book/?p=chapt06/06-07-memory-leaks

 

https://github.com/Leon2012/gimg

 

http://www.cnblogs.com/si-ren/archive/2010/11/08/2447695.html

 

https://github.com/buaazp/zimg

 

http://blog.csdn.net/lgg201/article/details/8806828

 

http://www.nowamagic.net/librarys/veda/detail/1441

 

http://huoding.com/2014/12/25/398

 

http://www.laruence.com/2009/11/27/1164.html

 

http://www.phppan.com/tag/php%E5%86%85%E5%AD%98%E6%B1%A0/

 

http://www.ibm.com/developerworks/cn/opensource/os-php-v521/

相關文章
相關標籤/搜索