1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
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;
//计算true_size,对于计算逻辑和取值可以参考上一篇文章
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;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
if (EXPECTED((true_size))) {
//根据true_size 计算 index的值
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
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
//在小内存块中查找
bitmap = heap->free_bitmap >> index;
if (bitmap) { //如果bitmap不等于0,说明存在大于或等于true_size的内存
/* Found some "small" free block that can be used */
index += zend_mm_low_bit(bitmap);//定位到true_size所在的index。举例说明:假设heap->free_bitmap=0b100010, index=3,则bitmap=(0b100),zend_mm_low_bit(bitmap)=3,最终index=6
best_fit = heap->free_buckets[index*2];//从free_buckets取出内存
#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);
//如果大内存中也没有找到,且real_size已经超过php内存限制,则到剩余内存中查找
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;
}
}
//以上四个区域均未找到
if (!best_fit) {
if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) {//true_size是否大于segment_size(block_size),小于,则申请segment_size,大于则申请小于true_size的segment_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;//如果keep_rest为1,则在后续处理剩余空闲内存时,将其放入剩余内存区域。这里的考虑应该是,当前剩余空闲内存会比较大。而且也会情况也会比较少,所以单独处理放入剩余内存区域(rest_buckets)
} 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统计字段
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;
//格式化申请的内存 为zend_mm_free_block
best_fit = (zend_mm_free_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE);
ZEND_MM_MARK_FIRST_BLOCK(best_fit);//初始化block中prev字段
//这里最后的ZEND_MM_ALIGNED_HEADER_SIZE是预留在最后的remaining_size使用的
block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE;
//初始化新segment的能存末位的 zend_mm_block
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;
//如果剩余内存小于block的头部大小,则将其合并到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 {//否则,将剩余内存放入到heap中
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)) {//这里请看上面代码中对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);
}
|