redis zmalloc
ddatsh
设计重要变化
zmalloc.h log from 2009/3/22
thread safe memory counter
2010/1/15 1.3
zcalloc
2010/07/25 2.2
memory fragmentation
2010/9/22 2.2
jemalloc
2011/6/2 2.6 forward-ported changes in zmalloc.c/h to support jemalloc build
后又 backported in 2.4
zmalloc_get_private_dirty
估算出子进程在持久化过程中实际消耗的物理内存大小
active memory defragmentation
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
- atomic(used_memory)
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
#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
#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 里类似
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
#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
void zmalloc_enable_thread_safeness(void);
static size_t used_memory = 0;
static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1;
}
redis.c 里
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
/* Fragmentation = RSS / allocated-bytes */
float zmalloc_get_fragmentation_ratio(void) {
#ifdef HAVE_PROCFS
/proc/%d/stat
mac 内存碎片率
// 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();
}
get rss,fragmentation拆分
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
#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
cache RSS in serverCron()
3.2
C11 __atomic
__sync_add_and_fetch
__sync_sub_and_fetch
to
__atomic_add_fetch
__atomic_sub_fetch
active memory defragmentation
void zfree_no_tcache(void *ptr);
void *zmalloc_no_tcache(size_t size);