欢迎转载,转载请注明出处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【找着处理】大块内存分配的时候,如果剩余的内存够下一次分配,就把剩余内存作为一个新的节点接回去对应大小的空闲队列里面,如果不够进行下一次分配,就直接把整块内存给申请者。注意返回的是可供程序使用的地址,而不是实际块的头部地址。