php内核中的内存分配 使用的函数有 emalloc(), erealloc() ,这两个函数分别是malloc(),realloc()函数的封装
关于内存分配有四个容器:cache,小块内存链表,大块内存(链表+树), 剩余rest内存链表
这四个窗器只保存所分配内存的大小,以及地址
zend_mm_seg_size这个环境变量默认为256K,当内核使用emalloc()申请内存时,php会调用malloc()申请256K大小的内存(256K内存使用mmap来分配,好处是,回收时直接给OS,不会被ptmalloc缓存)
将内核申请的内存返回给它后,剩余的内存根据情况 放入 上面的四个容器中
假设现在申请9字节内存,那么由于内存对齐,实际上要分配24字节
如果申请1字节内存,那么实际会返回16字节
php最小分配内存为272B
#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) #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
#define ZEND_MM_ALIGNED_FREE_HEADER_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block)) //16
#define ZEND_MM_ALIGNED_SIZE(size) \ (((size) + ZEND_MM_ALIGNMENT - 1) & ZEND_MM_ALIGNMENT_MASK) === (((size) + 8 - 1) & -8) #define ZEND_MM_MIN_ALLOC_BLOCK_SIZE \ //16 ZEND_MM_ALIGNED_SIZE(ZEND_MM_ALIGNED_HEADER_SIZE + END_MAGIC_SIZE) === ZEND_MM_ALIGNED_SIZE(8 + 4)
# define END_MAGIC_SIZE sizeof(unsigned int)
=== 4 #define ZEND_MM_ALIGNED_HEADER_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block)) //8 sizeof(zend_mm_block) 8 # define ZEND_MM_ALIGNMENT 8 #define ZEND_MM_ALIGNMENT_MASK ~(ZEND_MM_ALIGNMENT-1)
//
#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+8+4)
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); 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))) {
//所请求的大小为小块内存,并找到相应bucket 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 } segment = (zend_mm_segment *) ZEND_MM_STORAGE_ALLOC(segment_size); if (!segment) { /* Storage manager cannot allocate memory */
#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 %lu bytes)", heap->real_size, __zend_filename, __zend_lineno, size); #else zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %lu bytes)", heap->real_size, size); #endif
return NULL; } heap->real_size += segment_size; if (heap->real_size > heap->real_peak) { heap->real_peak = heap->real_size; } segment->size = segment_size; segment->next_segment = heap->segments_list; heap->segments_list = segment; best_fit = (zend_mm_free_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); ZEND_MM_MARK_FIRST_BLOCK(best_fit); block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE; ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(best_fit, block_size)); } 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); } 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); /* 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(); return ZEND_MM_DATA_OF(best_fit); }
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; }
参考文章
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/