ZendMM

ZendMM

1, php内存管理内置函数

void *emalloc(size_t size)                                       分配 size 字节的内存。
void *ecalloc(size_t nmemb, size_t size)                         给 nmemb 元素分配 size 字节的缓冲区并初始化为零。
void *erealloc(void *ptr, size_t size)                           修改使用 emalloc 分配的缓冲区 ptr 的大小为 size 字节。 
void efree(void *ptr)                                            释放 ptr 指向的缓冲区。缓冲区必须是由 emalloc 分配的。
void *safe_emalloc(size_t nmemb, size_t size, size_t offset)     
char *estrdup(const char *s)            分配一个可存放 NULL 结尾的字符串 s 的缓冲区,并将 s复制到缓冲区内。                         
char *estrndup(const char *s, unsigned int length) 类似于 estrdup,但 NULL 结尾的字符串长度是已知的。

2,由emalloc分配的内存都是在zend_mm_heap上

//zend_mm_mem_handlers
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;

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

//zend_mm_storage结构
struct _zend_mm_storage {
    const zend_mm_mem_handlers *handlers;//zend_mm_storage方法 (ZEND_MM_MEM_MALLOC_DSC)
    void *data;
};

//zend_mm_heap结构
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
};

//zend_alloc_globals结构
typedef struct _zend_alloc_globals {
    zend_mm_heap *mm_heap;
} 

//AG宏定义
# define AG(v) TSRMG(alloc_globals_id, zend_alloc_globals *, v)

//emalloc函数原型
ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
    TSRMLS_FETCH();

    if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
        return AG(mm_heap)->_malloc(size);
    }
    return _zend_mm_alloc_int(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

3,ZendMM环境变量

USE_ZEND_ALLOC 是否使用 ZendMM 进行内存管理。 ZEND_MM_MEM_TYPE 指定内存分配的方案,默认 malloc ZEND_MM_COMPACT 指定压缩边界值

//默认ZEND_MM_MEM_TYPE初始化

# 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}

# define ZEND_MM_MEM_WIN32_DSC {"win32", zend_mm_mem_win32_init, zend_mm_mem_win32_dtor, zend_mm_mem_win32_compact, zend_mm_mem_win32_alloc, zend_mm_mem_win32_realloc, zend_mm_mem_win32_free}

# define ZEND_MM_MEM_MMAP_ZERO_DSC {"mmap_zero", zend_mm_mem_mmap_zero_init, zend_mm_mem_mmap_zero_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_zero_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}

# define ZEND_MM_MEM_MMAP_ANON_DSC {"mmap_anon", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_anon_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}

4,ZendMM 流程

->SAPI
->php_module_startup (main.c 2065)
->zend_startup() //zend引擎启动
->start_memory_manager(TSRMLS_C) //开始内存管理 
->ts_allocate_id(&alloc_globals_id, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor) //初始化AG

->alloc_globals_ctor(&alloc_globals) //申请内存

    //分配内存,读取环境变量USE_ZEND_ALLOC,如果USE_ZEND_ALLOC==0 则禁用 ZendMMApi(emalloc/efree/...)
    //初始化mm_heap的代码(USE_ZEND_ALLOC != 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;

    //初始化mm_heap的代码(USE_ZEND_ALLOC != 0), 则启用 ZendMMApi(emalloc/efree/...)
        alloc_globals->mm_heap = zend_mm_startup();

->zend_mm_startup()     //读取内存分配方案(默认malloc)
->zend_mm_startup_ex()  //实例化zend_mm_heap分配内存 (heap = malloc(sizeof(struct _zend_mm_heap));)

->emalloc()             //申请内存,
                        //zend_mm_heap->use_zend_alloc==0 调用 mm_heap->_malloc方法初始化分配内存
                        //zend_mm_heap->use_zend_alloc!=0 调用 _zend_mm_alloc_int
->_zend_mm_alloc_int()
    ->ZEND_MM_STORAGE_ALLOC(segment_size) // 
        -> heap->storage->handlers->_alloc

            //ZEND_MM_MEM_WIN32_DSC
            ->zend_mm_mem_win32_alloc()
            ->HeapAlloc((HANDLE)storage->data, HEAP_NO_SERIALIZE, size)//最终把内存分配到了heap->storage->data

            //ZEND_MM_MEM_MALLOC_DSC
            ->zend_mm_mem_malloc_alloc()
            ->return (zend_mm_segment*)malloc(size) //最终把内存分配到了 heap->segments_list

            //ZEND_MM_MEM_MMAP_ZERO_DSC
            ->zend_mm_mem_mmap_zero_alloc()
            ->(zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zend_mm_dev_zero_fd, 0) //通过mmap把内存影射到heap->storage

            //ZEND_MM_MEM_MMAP_ANON_DSC
            ->zend_mm_mem_mmap_anon_alloc
            ->(zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0) 
            //最终把内存分配到了 heap->segments_list

->alloc_globals_dtor() //释放内存
->shutdown_memory_manager (main.c 2410) //结束内存管理
->zend_mm_shutdown(AG(mm_heap), full_shutdown, silent TSRMLS_CC) //最终调用释放内存的方法

5,总结

a) ZEND_MM_MEM_WIN32_DSC|ZEND_MM_MEM_MMAP_ZERO_DSC 内存保存到了 heap->storage ZEND_MM_MEM_MMAP_ANON_DSC|ZEND_MM_MEM_MALLOC_DSC 内存保存到了 heap->segments_list

b)