Redis 内存分配及释放相关源码阅读笔记,源码文件 zmalloc.h
& zmalloc.c
。
- 封装
tcmalloc
, jemalloc
, libmalloc
, ptmalloc
或自定义内存管理,增加内存使用量统计
- 在使用针对 Redis 修改的特殊
jemalloc
时,才能启用 Redis 内存碎片整理功能
分配内存
zmalloc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
// HAVE_MALLOC_SIZE 为 1 时,PREFIX_SIZE 为 0,直接返回 ptr
return ptr;
#else
// 保存数据所需分配内存的实际大小
*((size_t*)ptr) = size;
// 记录内存使用情况,更新 used_memory
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
// 首部 PREFIX_SIZE 字节中中存储了 size 的大小
return (char*)ptr+PREFIX_SIZE;
#endif
}
|
-
zmalloc_size
使用 tcmalloc
, jemalloc
, libmalloc
或 ptmalloc
时,HAVE_MALLOC_SIZE
为 1
不同内存分配函数的区别,可以参考这篇博客。
自身定义的 zmalloc_size
函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/* Provide zmalloc_size() for systems where this function is not provided by
* malloc itself, given that in that case we store a header with this
* information as the first bytes of every allocation. */
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr) {
void *realptr = (char*)ptr-PREFIX_SIZE;
size_t size = *((size_t*)realptr);
/* Assume at least that all the allocations are padded at sizeof(long) by
* the underlying allocator. (内存对齐) */
if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
return size+PREFIX_SIZE;
}
size_t zmalloc_usable(void *ptr) {
return zmalloc_size(ptr)-PREFIX_SIZE;
}
#endif
|
-
update_zmalloc_stat_alloc
1
2
3
4
5
6
|
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
// atomicIncr(var,count)
atomicIncr(used_memory,__n); \
} while(0)
|
根据 redis #4770,第 2、3 两行可以删除。目前由于并未计算内存补齐的原因,全局变量 used_memory
一般小于实际分配内存大小。
Tips: do {} while(0)
的作用:
- 辅助定义复杂的宏,避免引用的时候出错
- 避免使用
goto
对程序流进行统一的控制,例如:使用 break
来代替 goto
,后续的处理工作在 while
之后,就能够达到同样的效果
- 避免空宏引起的
warning
- 定义一个单独的函数块来实现复杂的操作
Tips: 将 $a$ 调整为 $b = 2^n$ 的整数倍
1
2
|
if (a & (b-1))
a += b - (a & (b-1));
|
zcalloc
源码逻辑和 zmalloc
一样,此处主要介绍 malloc
和 calloc
的区别:
Prototype |
effect |
void* malloc(size_t size); |
分配 size 字节未初始化的内存空间 |
void* calloc(size_t num, size_t size); |
为 num 个大小为 size 的对象分配内存空间,并且初始化每一 bit 为 0 |
两个函数都是线程安全的,并且都满足内存对齐,当分配成功时,返回指向初始位置的指针,该指针需要调用 free()
或 realloc()
释放,当分配失败时,返回空指针。
zrealloc
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
|
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr;
// 重新分配的内存的大小为 0
if (size == 0 && ptr != NULL) {
zfree(ptr);
return NULL;
}
if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom_handler(size);
// 记录内存使用情况,更新 used_memory
update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(zmalloc_size(newptr));
// HAVE_MALLOC_SIZE 为 1 时,PREFIX_SIZE 为 0,直接返回 newptr
return newptr;
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr); // 对应 *((size_t*)ptr) = size;
newptr = realloc(realptr,size+PREFIX_SIZE);
if (!newptr) zmalloc_oom_handler(size);
*((size_t*)newptr) = size;
// 记录内存使用情况,更新 used_memory
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
// 首部 PREFIX_SIZE 字节中中存储了 size 的大小
return (char*)newptr+PREFIX_SIZE;
#endif
}
|
释放内存
zfree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif
if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
// 记录内存使用情况,更新 used_memory
update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
// 记录内存使用情况,更新 used_memory
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}
|
Utils
复制字符串
1
2
3
4
5
6
7
8
|
char *zstrdup(const char *s) {
size_t l = strlen(s)+1; // '\0' 结尾
char *p = zmalloc(l);
// 调用字符串复制函数 void* memcpy(void *dest, const void *src, size_t count);
memcpy(p,s,l);
return p;
}
|
获取 used_memory
1
2
3
4
5
6
|
size_t zmalloc_used_memory(void) {
size_t um;
// atomicGet(var,dstvar)
atomicGet(used_memory,um);
return um;
}
|
设置异常处理函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 默认异常处理函数如下
static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
// 设置异常处理函数
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
zmalloc_oom_handler = oom_handler;
}
|