redis zmalloc
dev
#redis
【redis】
设计重要变化
zmalloc.h log from 2009/3/22
2010/1/15 1.3
2010/07/25 2.2
2010/9/22 2.2
2011/6/2 2.6
forward-ported changes in zmalloc.c/h to support jemalloc build
后又 backported in 2.4
估算出子进程在持久化过程中实际消耗的物理内存大小
2016/12/30 5.0
purge jemalloc after flush
2019/5/30 6.0
madvise(MADV_DONTNEED)
2021/08/05 7.0 Use madvise(MADV_DONTNEED) to release memory to reduce COW (#8974)…
extend_to_usable
2023/4/11 7.2 Use dummy allocator to make accesses defined as per standard (#11982)
解决 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 时报错
gcc Function Attributes alloc_size
1
2
3
4
5
6
7
8
9
|
static redisAtomic size_t used_memory = 0;
#define update_zmalloc_stat_alloc(__n) atomicIncr(used_memory,(__n))
#define update_zmalloc_stat_free(__n) atomicDecr(used_memory,(__n))
#define atomicIncr(var,count) atomic_fetch_add_explicit(&var,(count),memory_order_relaxed)
#define atomic_fetch_add_explicit(PTR, VAL, MO) \
__atomic_fetch_add ((PTR), (VAL), (MO))
|
源码历程
first commit
zmalloc.h
1
2
3
4
5
6
7
8
9
10
|
#ifndef _ZMALLOC_H
#define _ZMALLOC_H
void *zmalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void *zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
#endif /* _ZMALLOC_H */
|
zmalloc.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
|
#include <stdlib.h>
#include <string.h>
static size_t used_memory = 0;
void *zmalloc(size_t size) {
void *ptr = malloc(size+sizeof(size_t));
*((size_t*)ptr) = size;
used_memory += size+sizeof(size_t);
return ptr+sizeof(size_t);
}
void *zrealloc(void *ptr, size_t size) {
void *realptr;
size_t oldsize;
void *newptr;
if (ptr == NULL) return zmalloc(size);
realptr = ptr-sizeof(size_t);
oldsize = *((size_t*)realptr);
newptr = realloc(realptr,size+sizeof(size_t));
if (!newptr) return NULL;
*((size_t*)newptr) = size;
used_memory -= oldsize;
used_memory += size;
return newptr+sizeof(size_t);
}
void zfree(void *ptr) {
void *realptr;
size_t oldsize;
if (ptr == NULL) return;
realptr = ptr-sizeof(size_t);
oldsize = *((size_t*)realptr);
used_memory -= oldsize+sizeof(size_t);
free(realptr);
}
char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l);
memcpy(p,s,l);
return p;
}
size_t zmalloc_used_memory(void) {
return used_memory;
}
|
ANSI-C compatibility changes
问 kimi
redis zmalloc 里类似
1
2
3
4
|
void *zmalloc(size_t size) {
void *ptr = malloc(size);
return (char*)ptr;
}
|
为什么返回转换成 (char*) 更符合 ansi c
- 类型安全:
char*
是一个具体的数据类型,可以被解引用并用于访问内存中的数据。在 ANSI C 中,char*
通常用于表示字符串,但也可以用于表示任意字节的内存块
- 兼容性:在某些情况下,可能需要将内存块作为字符串来处理,比如使用标准库函数如
strlen
、strcpy
等。将 void*
转换为 char*
可以提供这种兼容性
- 编码风格:在某些编码风格或项目中,可能习惯于使用
char*
来表示内存块,这样可以在不改变其他代码的情况下重用现有的代码
- 避免警告:在某些编译器或严格的编译选项下,直接使用
void*
可能会引起警告,因为编译器无法确定内存块的大小。转换为 char*
可以消除这种警告
- 方便操作:
char*
可以方便地进行字节操作,比如通过索引来访问和修改内存块中的特定字节
实际使用中,应该根据上下文来决定是否需要这种转换。如果只是简单地分配内存,而不需要进行字节操作或与字符串相关的操作,那么这种转换可能是不必要的
zmalloc fix, return NULL or real malloc failure
macosx malloc_size
macosx specific zmalloc.c, uses malloc_size function in order to avoid to waste memory and time to put an additional header
config.h
1
2
3
4
5
|
#ifdef __APPLE__
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE
#define redis_malloc_size(p) malloc_size(p)
#endif
|
oom check
著名的 PREFIX_SIZE
zmalloc Solaris fixes
thread safe counter
1
|
void zmalloc_enable_thread_safeness(void);
|
1
2
3
|
static size_t used_memory = 0;
static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
|
1
2
3
|
void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1;
}
|
redis.c 里
1
2
3
4
|
static void vmInit(void) {
...
if (server.vm_max_threads != 0)
zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */
|
vm 之后就废弃了
内存对齐
zcalloc
合适的场景使用时,calloc 可能 malloc+memset 更高效
commit log 里有段详述
内存碎片率
memory fragmentation ratio in INFO output
1
2
3
4
5
|
/* Fragmentation = RSS / allocated-bytes */
float zmalloc_get_fragmentation_ratio(void) {
#ifdef HAVE_PROCFS
/proc/%d/stat
|
mac 内存碎片率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// linux
#if defined(HAVE_PROCFS)
#elif defined(HAVE_TASKINFO)
float zmalloc_get_fragmentation_ratio(void) {
task_t task = MACH_PORT_NULL;
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
return 0;
task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
return (float)t_info.resident_size/zmalloc_used_memory();
}
|
1
2
3
|
float zmalloc_get_fragmentation_ratio(void) {
return (float)zmalloc_get_rss()/zmalloc_used_memory();
}
|
开始引入jemalloc
since 2.6
buildin atomic
zmalloc_get_private_dirty
3.0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#if defined(HAVE_PROCFS)
size_t zmalloc_get_private_dirty(void) {
char line[1024];
size_t pd = 0;
FILE *fp = fopen("/proc/self/smaps","r");
if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) {
if (strncmp(line,"Private_Dirty:",14) == 0) {
char *p = strchr(line,'k');
if (p) {
*p = '\0';
pd += strtol(line+14,NULL,10) * 1024;
}
}
}
fclose(fp);
return pd;
}
#else
size_t zmalloc_get_private_dirty(void) {
return 0;
}
#endif
|
3.2
C11 __atomic
1
2
|
__sync_add_and_fetch
__sync_sub_and_fetch
|
to
1
2
|
__atomic_add_fetch
__atomic_sub_fetch
|
active memory defragmentation
1
2
|
void zfree_no_tcache(void *ptr);
void *zmalloc_no_tcache(size_t size);
|