欢迎转载,转载请注明出处http://blog.csdn.net/hackooo/article/details/8702156 谢谢!新浪微博:小灰马
主要内容
typedef struct _zend_mm_segment { size_t size; struct _zend_mm_segment *next_segment; } zend_mm_segment;
//大块内存小块内存都有的头部 zend_mm_block_info typedef struct _zend_mm_block_info { #if ZEND_MM_COOKIES size_t _cookie; #endif size_t _size; size_t _prev; } zend_mm_block_info; //小块内存:zend_mm_small_free_block typedef struct _zend_mm_small_free_block { zend_mm_block_info info; #if ZEND_DEBUG unsigned int magic; # ifdef ZTS THREAD_T thread_id; # endif #endif struct _zend_mm_free_block *prev_free_block; struct _zend_mm_free_block *next_free_block; } zend_mm_small_free_block;
//大块内存:zend_mm_free_block typedef struct _zend_mm_free_block { zend_mm_block_info info; #if ZEND_DEBUG unsigned int magic; # ifdef ZTS THREAD_T thread_id; # endif #endif 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
大块内存:zend_mm_free_block的逻辑结构图
字节范围 |
范围大小(多少个节点) |
箱号 |
[256, 384) 左子树(实际上应该加个头部,比256大,这里只取256是为了说明其基本原理) 二进制 [0000 0000 0000 0000 0000 0001 0000 0000 至 0000 0000 0000 0000 0000 0001 0111 1111) |
128 |
8 |
[384, 512) 右子树 二进制 [0000 0000 0000 0000 0000 0001 1000 0000 至 0000 0000 0000 0000 0000 0001 1111 1111) |
128 |
8 |
[512, 768) 依次类推 |
256 |
9 |
[768, 1024) |
256 |
9 |
[1024, 1536) |
512 |
… |
…… |
…… |
…… |
…… |
…… |
…… |
[6291456, 8388608) |
2097152 |
… |
[8388608, 12582912) |
4194304 |
31 |
[12582912, 与size_t相关) 二进制 [1100 0000 0000 0000 0000 0000 0000 0000 至 1111 1111 1111 1111 1111 1111 1111 1111) |
与size_t相关 |
31 |
struct _zend_mm_heap { int use_zend_alloc; void *(*_malloc)(size_t); void (*_free)(void*); void *(*_realloc)(void*, size_t); size_t free_bitmap; //小块内存的32个头结点里面是否有空闲几点的标记位图,每一位标记一个头结点 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];//cache的统计信息,前面32个用来统计命中缓存对应index的数目,最后一个用来统计命中小块内存的数目,看看_zend_mm_alloc_int就知道啦 #endif };
基本步骤
1.计算申请的size实际得占用的内存块大小true_size
2. 如果是小块内存
2.1 到cache队列找找有木有匹配的,有的话把第一个合适的节点摘下来,返回,否则下一步
2.2 到heap->free_buckets里找最接近的块,成功直接返回,否则下一步
3.如果申请的不是小块内存,到heap->large_free_buckets找最合适的
3.1 如果找着合适的,就把它从树上摘下来,进行【找着处理】
3.2 如果在large_free_buckets没找着,而且发现heap->real_size接近heap->limit了,那说明可分配的内存快用完了,那就去备胎中心rest_buckets找一个大小最接近的块,进行【找着处理】,否则下一步。
3.3 尝试去堆里分配一块segment出来,segment_size<=heap->block_size。分配成功的话,把新的segment加入到原来heapd的segment队列里,进行【找着处理】,否则清空cache,申请失败,拜拜!
3.4【找着处理】大块内存分配的时候,如果剩余的内存够下一次分配,就把剩余内存作为一个新的节点接回去对应大小的空闲队列里面,如果不够进行下一次分配,就直接把整块内存给申请者。注意返回的是可供程序使用的地址,而不是实际块的头部地址。