php 内存分配
php内核中的内存分配 使用的函数有 emalloc(), erealloc() ,这两个函数分别是malloc(),realloc()函数的封装
关于内存分配有四个容器:cache,小块内存链表,大块内存(链表+树), 剩余rest内存链表
大块内存(链表+二叉排序树):链表中每个bucket除了有前继结点,后继结点外,还是二叉树的root结点, 经过内存对齐后, 可分配内存的大小是8的倍数
例如 40的 二进制是 101000,位于大块内存中链表的第5个bucket(从右往左数,最高位是1, 处于第5位了)
假设现在第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位,则去掉最高位
先左移26位
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位了,所以直接舍弃第一个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 | 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 + - ) & (~ ( - ) );
}
for($i=;$i<;$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 < ) ? : ZEND_MM_ALIGNED_SIZE(size++) #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)) :) ZEND_MM_MIN_SIZE = ( > ( + )) ? -(+): = ZEND_MM_ALIGNED_MIN_HEADER_SIZE
ZEND_MM_ALIGNED_HEADER_SIZE
ZEND_MM_MIN_ALLOC_BLOCK_SIZE
# define END_MAGIC_SIZE sizeof(unsigned int) #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) //
ZEND_MM_ALIGNED_MIN_HEADER_SIZE = ( > ) ? : = #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( + ) = #define ZEND_MM_ALIGNED_HEADER_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block))
ZEND_MM_ALIGNED_HEADER_SIZE = ZEND_MM_ALIGNED_SIZE() = #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() = /**
*传入的大小内存以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[];
} 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*];
zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];
zend_mm_free_block *rest_buckets[];
int rest_count;
#if ZEND_MM_CACHE_STAT
struct {
int count;
int max_count;
int hit;
int miss;
} cache_stat[ZEND_MM_NUM_BUCKETS+];
#endif
};
2.
#define ZEND_MM_SMALL_FREE_BUCKET(heap, index)
(zend_mm_free_block*) ( (char*)&heap->free_buckets[index * ] + sizeof(zend_mm_free_block*) * - 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[] != 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() << index);
}
} else {
while (*(cp = &(prev->child[prev->child[] != 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[] = mm_block->child[])) {
ZEND_MM_CHECK_TREE(prev->child[]);
prev->child[]->parent = &prev->child[];
}
if ((prev->child[] = mm_block->child[])) {
ZEND_MM_CHECK_TREE(prev->child[]);
prev->child[]->parent = &prev->child[];
}
}
} 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*] == heap->free_buckets[index*+])) {
//设置heap->free_bitmap 位图,将第index个桶的位 置0,表示这个桶没有可使用的内存
heap->free_bitmap &= ~(ZEND_MM_LONG_CONST() << 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[] = mm_block->child[] = 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() << 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 <<= ) {
zend_mm_free_block *prev = *p; if (ZEND_MM_FREE_BLOCK_SIZE(prev) != size) {
p = &prev->child[(m >> (ZEND_MM_NUM_BUCKETS-)) & ];
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() << 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 = ; 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, , );
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*];
#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[];
size_t best_size = -; 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-)) & ~(heap->block_size-);
keep_rest = ;
} 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, , ); 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 == ) {
return NULL;
} if (UNEXPECTED((bitmap & ) != )) {
/* Search for best "large" free block */
zend_mm_free_block *rst = NULL;
size_t m;
size_t best_size = -; best_fit = NULL; //取出头结点
p = heap->large_free_buckets[index];
for (m = true_size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= ) {
//如果该结点的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() << (ZEND_MM_NUM_BUCKETS-))) == ) {
//移位,如果与m做&运算为0,说明在左孩子下面,同时右孩子做备胎
if (p->child[]) {
rst = p->child[];
}
if (p->child[]) {
p = p->child[];
} else {
break;
}
} else if (p->child[]) {
//移位,如果与m做&运算为1,说明在右孩子下面
p = p->child[];
} else {
break;
}
} for (p = rst; p; p = p->child[p->child[] != 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 >> ;
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[] != 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, , );
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, , ); 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, , );
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-)) & ~(heap->block_size-);
} 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, , ); 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, )) {
alloc_globals->mm_heap = malloc(sizeof(struct _zend_mm_heap));
memset(alloc_globals->mm_heap, , sizeof(struct _zend_mm_heap));
alloc_globals->mm_heap->use_zend_alloc = ;
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 = ;
} 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, );
...
//使用指定的分配内存大小
} 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, , 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 == ) {
zend_mm_random((unsigned char*)&_mem_block_start_magic, sizeof(_mem_block_start_magic));
}
if (_mem_block_end_magic == ) {
zend_mm_random((unsigned char*)&_mem_block_end_magic, sizeof(_mem_block_end_magic));
}
#endif
#if ZEND_MM_COOKIES
if (_zend_mm_cookie == ) {
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();
}
/*
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();
}
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();
}
heap->storage = storage;
heap->block_size = block_size;
heap->compact_size = ;
heap->segments_list = NULL;
zend_mm_init(heap);
# if ZEND_MM_CACHE_STAT
memset(heap->cache_stat, , sizeof(heap->cache_stat));
# endif heap->use_zend_alloc = ;
heap->real_size = ;
heap->overflow = ;
heap->real_peak = ;
heap->limit = ZEND_MM_LONG_CONST()<<(ZEND_MM_NUM_BUCKETS-);
heap->size = ;
heap->peak = ;
heap->internal = internal;
heap->reserve = NULL;
heap->reserve_size = reserve_size;
if (reserve_size > ) {
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 = ;
heap->large_free_bitmap = ;
#if ZEND_MM_CACHE
heap->cached = ;
memset(heap->cache, , sizeof(heap->cache));
#endif
#if ZEND_MM_CACHE_STAT
for (i = ; i < ZEND_MM_NUM_BUCKETS; i++) {
heap->cache_stat[i].count = ;
}
#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, );
for (i = ; 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*) * );
heap->large_free_buckets[i] = NULL;
}
heap->rest_buckets[] = heap->rest_buckets[] = ZEND_MM_REST_BUCKET(heap);
heap->rest_count = ;
} 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/
php 内存分配的更多相关文章
- 《深入理解Java虚拟机》内存分配策略
上节学习回顾 1.判断对象存活算法:引用计数法和可行性分析算法 2.垃圾收集算法:标记-清除算法.复制算法.标记-整理算法 3.垃圾收集器: Serial:新生代收集器,采用复制算法,单线程. Par ...
- Java的内存分配
java内存分配 A:栈 存储局部变量 B:堆 存储所有new出来的 C:方法区(方法区的内存中) 类加载时 方法信息保存在一块称为方法区的内存中, 并不随你创建对象而随对象保存于堆中; D:本地方法 ...
- C语言内存分配方法。
当C程序运行在操作系统上时,操作系统会给每一个程序分配一定的栈空间. 堆为所有程序共有的,需要时需要申请访问. 一.栈 局部变量.函数一般在栈空间中. 运行时自动分配&自动回收:栈是自动管理的 ...
- JVM内存分配策略
在 JVM内存垃圾回收方法 中,我们已经详细讨论了内存回收,但是,我们程序中生成的对象是如何进行分配的呢?以下所述针对的是HotSpot虚拟机. 1.Java堆结构 以HotSpot为例,如下图: H ...
- Java的垃圾回收和内存分配策略
本文是<深入理解Java虚拟机 JVM高级特性与最佳实践>的读书笔记 在介绍Java的垃圾回收方法之前,我们先来了解一下Java虚拟机在执行Java程序的过程中把它管理的内存划分为若干个不 ...
- Buddy内存分配算法
Buddy(伙伴的定义): 这里给出伙伴的概念,满足以下三个条件的称为伙伴:1)两个块大小相同:2)两个块地址连续:3)两个块必须是同一个大块中分离出来的: Buddy算法的优缺点: 1)尽管伙伴内存 ...
- 小白请教几个关于Java虚拟机内存分配策略的问题
最近在看周志明所著的<深入理解Java虚拟机>,有几个问题不太明白,希望对虚拟机有研究的哥们儿帮我解答一下.先说一下我进行试验的环境: 操作系统:Mac OS X 10.11.6 EI C ...
- Linux内核笔记--内存管理之用户态进程内存分配
内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...
- Linux内核笔记——内存管理之块内存分配
内核版本:linux-2.6.11 伙伴系统 伙伴系统是linux用于满足对不同大小块物理内存分配和释放请求的解决方案. 内存管理区 linux将物理内存分成三个内存管理区,分别为ZONE_DMA Z ...
- java中内存分配策略及堆和栈的比较
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
随机推荐
- python-opencv 图像二值化,自适应阈值处理
定义:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果. 一幅图像包括目标物体.背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用 ...
- CentOS服务器简单判断CC攻击的命令
使用下面的命令,可以分析下是否在被CC攻击. 第一条命令: tcpdump -s0 -A -n -i any | grep -o -E '(GET|POST|HEAD) .*' 正常的输出结果类似 ...
- [算法]从一道题引出variable-precision SWAR算法
苏君君出了一道题,是牛客网上面的: 输入一个int型整数,输出该数二进制表示中1的个数.其中负数用补码表示. 其实这道题并不难,大家很容易想到的解法是转成字符串的思路,即如下所示: public st ...
- js 获取页面宽度
特例: 当$(window).width()无效时 /* 出现时机: iframe内嵌子页面在加载过程中取不到$(window).width(),非必现,机率大概1 / 20 */ 可用以下方式获取屏 ...
- 百度地图Api进阶教程-默认控件和自定义控件2.html
<!DOCTYPE html> <html> <head> <meta name="viewport" content="ini ...
- 在kali linux之下 下载并解压的文件名呈现乱码 解决方案
从Linux往 windows拷贝文件或者从windows往Linux拷贝文件,有时会出现中文文件名乱码的情况,出现这种问题的原因是因为,windows的文件名中文编码默认为GBK,而Linux中默认 ...
- Ubuntu下Ruby的下载和编译源码安装
1.Ruby的下载 Ruby可以在Ruby 官网上下载,如果想获取更多的Ruby版本,可以到淘宝镜像网站下载. 2.Ruby的编译源码安装 解压 首先把下载下来的源码压缩包解压到自己指定的目录 编译安 ...
- App Store那些事儿
5条建议优化手机游戏的苹果App Store截图 第一:遵守应用商店的规定 进入应用商店的每一款应用都要加上至少一张截图,并且尺寸大小必须符合应用商店的惯例. 第二:显示营销信息 既然你已经知道规定的 ...
- (笔记)Linux常用命令大全
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- VIM下的可视模式的相关知识
三种可视模式: v 激活面向字符的可视模式: V 激活面向行的可视模式: ctrl+v 激活面向列块的可视模式: 选择高亮区: 上面的 v 是可以与跳转指令 以及表示范围的指令组合使用的. 如:vl, ...