cpp-doc-内存池

传统内存操作

1
2
3
4
5
6
7
8
9
10
11
// 在内存的动态存储区中分配一块长度为size字节的连续区域返回该区域的首地址.
void *malloc(size_t size);

// 与malloc相似,参数size为申请地址的单位元素长度,nmemb为元素个数,即在内存中申请nmemb*size字节大小的连续地址空间.内存会初始化0
void *calloc(size_t nmemb, size_t size);

// 给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度.ptr 若为NULL,它就等同于 malloc.
void *realloc(void *ptr, size_t size);

// 释放内存
void free(void *ptr);

弊端

  • 高并发时较小内存块使用导致系统调用频繁,降低了系统的执行效率

  • 频繁使用时增加了系统内存的碎片,降低内存使用效率

内部碎片: 已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间;

产生根源:

  1. 内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址
  2. MMU的分页机制的限制

  • 没有垃圾回收机制,容易造成内存泄漏,导致内存枯竭
1
2
3
4
5
6
void log_error(char *reason) { 
char *p1;
p1 = malloc(100);
sprintf(p1,"The f1 error occurred because of '%s'.", reason);
log(p1);
}
1
2
3
4
5
6
7
8
int getkey(char *filename) { 
FILE *fp;
int key;
fp = fopen(filename, "r");
fscanf(fp, "%d", &key);
//fclose(fp);
return key;
}
  • 内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性
1
2
3
4
5
6
7
8
9
ret get_stu_info(  Student  * _stu  ) { 
char * name= NULL;
name = getName(_stu->no);
// 处理逻辑
if(name) {
free(name);
name = NULL;
}
}
1
2
3
4
5
6
7
char stu_name[MAX];

char * getName(int stu_no){
// 查找相应的学号并赋值给 stu_name
snprintf(stu_name,MAX,“%s”,name);
return stu_name;
}

内存管理组建对比

PtMalloc TcMalloc JeMalloc
概念 Glibc自带 Goggle 开源 Jason Evans(FreeBSD人员)
性能
(一次malloc/free操作)
300ns 50ns <=50ns
弊端 锁机制降低性能,容易导致内存碎片 1%左右的额外内存开销 2%左右的额外内存开销
优点 传统,稳定 线程本地缓存,多线程分配效率高 线程本地缓存,多核多线程分配效率相当高
使用方式 Glibc 编译 动态链接库 动态链接库
使用情况 较普遍 safari、chrome等 facebook、firefox 等
适用场景 除特别追求高效内存分配以外的 多线程下高效内存分配 多线程下高效内存分配

内存池

概念

什么是内存池技术?
在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,统一对程序所使用的内存进行统一的分配和回收。这样做的一个显著优点是,使得内存分配效率得到很大的提升。

传统弊端解决

高并发时系统调用频繁,降低了系统的执行效率
内存池提前预先分配大块内存,统一释放,极大的减少了malloc 和 free 等函数的调用。

频繁使用时增加了系统内存的碎片,降低内存使用效率
内存池每次请求分配大小适度的内存块,避免了碎片的产生

没有垃圾回收机制,容易造成内存泄漏
在生命周期结束后统一释放内存,完全避免了内存泄露的产生

内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性
在生命周期结束后统一释放内存,避免重复释放指针或释放空指针等情况

实现思路

对于每个请求或者连接都会建立相应的内存池,建立好内存池之后,我们可以直接从内存池中申请所需要的内存,不用去管内存的释放,当内存池使用完成之后一次性销毁内存池。

区分大小内存块的申请和释放,大于池尺寸的定义为大内存块,使用单独的大内存块链表保存,即时分配和释放;小于等于池尺寸的定义为小内存块,直接从预先分配的内存块中提取,不够就扩充池中的内存,在生命周期内对小块内存不做释放,直到最后统一销毁。

nginx 内存池

Nginx 内存池结构图
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct {
u_char *last; // 保存当前数据块中内存分配指针的当前位置
u_char *end; // 保存内存块的结束位置
ngx_pool_t *next; // 内存池由多块内存块组成,指向下一个数据块的位置
ngx_uint_t failed; // 当前数据块内存不足引起分配失败的次数
} ngx_pool_data_t;

struct ngx_pool_s {
ngx_pool_data_t d; // 内存池当前的数据区指针的结构体
size_t max; // 当前数据块最大可分配的内存大小(Bytes)
ngx_pool_t *current; // 当前正在使用的数据块的指针
ngx_pool_large_t *large; // pool 中指向大数据块的指针(大数据快是指 size > max 的数据块)
};
关键数据结构

ngx_pool_t 结构示意图(大小为1024)

操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 创建内存池	
ngx_pool_t * ngx_create_pool(size_t size);

// 销毁内存池
void ngx_destroy_pool(ngx_pool_t *pool);

// 重置内存池
void ngx_reset_pool(ngx_pool_t *pool);

// 内存申请(对齐)
void * ngx_palloc(ngx_pool_t *pool, size_t size);

// 内存申请(不对齐)
void * ngx_pnalloc(ngx_pool_t *pool, size_t size);

// 内存申请(对齐并初始化)
void * ngx_pcalloc(ngx_pool_t *pool, size_t size);

// 内存清除
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);

源码

mem_pool_palloc.h
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

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/


#ifndef _NGX_PALLOC_H_INCLUDED_
#define _NGX_PALLOC_H_INCLUDED_


#include "mem_core.h"


/*
* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
* On Windows NT it decreases a number of locked pages in a kernel.
*/
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)

#define NGX_DEFAULT_POOL_SIZE (16 * 1024)

#define NGX_POOL_ALIGNMENT 16
#define NGX_MIN_POOL_SIZE \
ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \
NGX_POOL_ALIGNMENT)




typedef struct ngx_pool_large_s ngx_pool_large_t;



struct ngx_pool_large_s {
ngx_pool_large_t *next; // 指向下一块大内存块的指针
void *alloc; // 大内存块的起始地址
};

typedef struct {
u_char *last; // 保存当前数据块中内存分配指针的当前位置。每次Nginx程序从内存池中申请内存时,
//从该指针保存的位置开始划分出请求的内存大小,并更新该指针到新的位置。
u_char *end; // 保存内存块的结束位置
ngx_pool_t *next; // 内存池由多块内存块组成,指向下一个数据块的位置。
ngx_uint_t failed; // 当前数据块内存不足引起分配失败的次数
} ngx_pool_data_t;

struct ngx_pool_s {
ngx_pool_data_t d; // 内存池当前的数据区指针的结构体
size_t max; // 当前数据块最大可分配的内存大小(Bytes)
ngx_pool_t *current; // 当前正在使用的数据块的指针
ngx_pool_large_t *large; // pool 中指向大数据块的指针(大数据快是指 size > max 的数据块)

};



void *ngx_alloc(size_t size);
void *ngx_calloc(size_t size);

ngx_pool_t *ngx_create_pool(size_t size);
void ngx_destroy_pool(ngx_pool_t *pool);
void ngx_reset_pool(ngx_pool_t *pool);

void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);


#endif /* _NGX_PALLOC_H_INCLUDED_ */
mem_pool_palloc.c
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/


#include "mem_core.h"


static inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size,
ngx_uint_t align);
static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);


ngx_pool_t *
ngx_create_pool(size_t size)
{
ngx_pool_t *p;

p = ngx_memalign(NGX_POOL_ALIGNMENT, size);
if (p == NULL) {
return NULL;
}

p->d.last = (u_char *) p + sizeof(ngx_pool_t);
p->d.end = (u_char *) p + size;
p->d.next = NULL;
p->d.failed = 0;

size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

p->current = p;
//p->chain = NULL;
p->large = NULL;
//p->cleanup = NULL;
//p->log = log;

return p;
}


void
ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
//ngx_pool_cleanup_t *c;

/*for (c = pool->cleanup; c; c = c->next) {
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}*/

#if (NGX_DEBUG)

/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/

for (l = pool->large; l; l = l->next) {
fprintf(stderr,"free: %p", l->alloc);
}

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
fprintf(stderr,"free: %p, unused: %zu", p, p->d.end - p->d.last);

if (n == NULL) {
break;
}
}

#endif

for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p);

if (n == NULL) {
break;
}
}
}


void
ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;

for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}

for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
p->d.failed = 0;
}

pool->current = pool;
//pool->chain = NULL;
pool->large = NULL;
}


void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 1);
}
#endif

return ngx_palloc_large(pool, size);
}


void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 0);
}
#endif

return ngx_palloc_large(pool, size);
}


static inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p;

p = pool->current;

do {
m = p->d.last;

if (align) {
m = ngx_align_ptr(m, NGX_ALIGNMENT);
}

if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;

return m;
}

p = p->d.next;

} while (p);

return ngx_palloc_block(pool, size);
}


static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;

psize = (size_t) (pool->d.end - (u_char *) pool);

m = ngx_memalign(NGX_POOL_ALIGNMENT, psize);
if (m == NULL) {
return NULL;
}

new = (ngx_pool_t *) m;

new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;

m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;

for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}

p->d.next = new;

return m;
}


static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large;

p = ngx_alloc(size);
if (p == NULL) {
return NULL;
}

n = 0;

for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}

if (n++ > 3) {
break;
}
}

large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}

large->alloc = p;
large->next = pool->large;
pool->large = large;

return p;
}


void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
void *p;
ngx_pool_large_t *large;

p = ngx_memalign(alignment, size);
if (p == NULL) {
return NULL;
}

large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}

large->alloc = p;
large->next = pool->large;
pool->large = large;

return p;
}


ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l;

for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
fprintf(stderr,"free: %p", l->alloc);
ngx_free(l->alloc);
l->alloc = NULL;

return NGX_OK;
}
}

return NGX_DECLINED;
}


void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p;

p = ngx_palloc(pool, size);
if (p) {
ngx_memzero(p, size);
}

return p;
}
men_core.h
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

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/


#ifndef _NGX_CORE_H_INCLUDED_
#define _NGX_CORE_H_INCLUDED_


#define NGX_HAVE_POSIX_MEMALIGN 1


typedef struct ngx_pool_s ngx_pool_t;


#define NGX_OK 0
#define NGX_ERROR -1
#define NGX_AGAIN -2
#define NGX_BUSY -3
#define NGX_DONE -4
#define NGX_DECLINED -5
#define NGX_ABORT -6



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <sys/stat.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>

typedef intptr_t ngx_int_t;
typedef uintptr_t ngx_uint_t;

#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

#define ngx_memzero(buf, n) (void) memset(buf, 0, n)

#include "mem_alloc.h"
#include "mem_pool_palloc.h"


#endif /* _NGX_CORE_H_INCLUDED_ */
men_alloc.h
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

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/


#ifndef _NGX_ALLOC_H_INCLUDED_
#define _NGX_ALLOC_H_INCLUDED_



#include "mem_core.h"


void *ngx_alloc(size_t size);
void *ngx_calloc(size_t size);

#define ngx_free free


/*
* Linux has memalign() or posix_memalign()
* Solaris has memalign()
* FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
* aligns allocations bigger than page size at the page boundary
*/

/*#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)

void *ngx_memalign(size_t alignment, size_t size);

#else
*/
#define ngx_memalign(alignment, size) ngx_alloc(size)
/*
#endif
*/

extern ngx_uint_t ngx_pagesize;
extern ngx_uint_t ngx_pagesize_shift;
extern ngx_uint_t ngx_cacheline_size;


#endif /* _NGX_ALLOC_H_INCLUDED_ */
men_alloc.c
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

/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/


#include "mem_core.h"

static int debug = 0;

ngx_uint_t ngx_pagesize;
ngx_uint_t ngx_pagesize_shift;
ngx_uint_t ngx_cacheline_size;



void *
ngx_alloc(size_t size)
{
void *p;

p = malloc(size);
if (p == NULL) {
fprintf(stderr,"malloc(%zu) failed", size);
}

if(debug) fprintf(stderr, "malloc: %p:%zu", p, size);

return p;
}


void *
ngx_calloc(size_t size)
{
void *p;

p = ngx_alloc(size);

if (p) {
ngx_memzero(p, size);
}

return p;
}

/*
#if (NGX_HAVE_POSIX_MEMALIGN)

void *
ngx_memalign(size_t alignment, size_t size)
{
void *p;
int err;

err = posix_memalign(&p, alignment, size);

if (err) {
fprintf(stderr,"posix_memalign(%zu, %zu) failed", alignment, size);
p = NULL;
}

if(debug) fprintf(stderr,"posix_memalign: %p:%zu @%zu", p, size, alignment);

return p;
}

#elif (NGX_HAVE_MEMALIGN)

void *
ngx_memalign(size_t alignment, size_t size)
{
void *p;

p = memalign(alignment, size);
if (p == NULL) {
fprintf(stderr,"memalign(%zu, %zu) failed", alignment, size);
}

if(debug) fprintf(stderr,"memalign: %p:%zu @%zu", p, size, alignment);

return p;
}

#endif
*/

使用

main.c
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
#include "mem_core.h"


#define BLOCK_SIZE 16 //每次分配内存块大小

#define MEM_POOL_SIZE (1024 * 4) //内存池每块大小


int main(int argc, char **argv)
{
int i = 0, k = 0;

ngx_pagesize = getpagesize();

char * ptr = NULL;

for(k = 0; k< 1024 * 500; k++)
{
ngx_pool_t * mem_pool = ngx_create_pool(MEM_POOL_SIZE);

for(i = 0; i < 1024 ; i++)
{
ptr = ngx_palloc(mem_pool,BLOCK_SIZE);

if(!ptr) fprintf(stderr,"ngx_palloc failed. \n");
else {
*ptr = '\0';
*(ptr + BLOCK_SIZE -1) = '\0';
}
}

ngx_destroy_pool(mem_pool);

return 0;
}
Makefile
1
2
3
4
5
6
7
8
9
10
11
memtest : main.o mem_alloc.o mem_pool_palloc.o  
cc -o memtest main.o mem_alloc.o mem_pool_palloc.o

main.o : main.c mem_core.h
cc -c main.c
mem_alloc.o : mem_alloc.c mem_core.h
cc -c mem_alloc.c
mem_pool_palloc.o : mem_pool_palloc.c mem_core.h
cc -c mem_pool_palloc.c
clean :
rm -fr memtest main.o mem_alloc.o mem_pool_palloc.o