Redis 源码阅读笔记(1)zmalloc:zmalloc 主要用于 Redis 的内存管理,实际上它是在 tcmalloc 和 jemalloc 以及 malloc 的基础上进行了重新封装,所以并不是全新的内存管理体系。
所有函数如下所列
- void * zmalloc(size_t size);
- void * zcalloc(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);
- void zmalloc_enable_thread_safeness(void);
- void zmalloc_set_oom_handler(void( * oom_handler)(size_t));
- float zmalloc_get_fragmentation_ratio(size_t rss);
- size_t zmalloc_get_rss(void);
- size_t zmalloc_get_private_dirty(long pid);
- size_t zmalloc_get_smap_bytes_by_field(char * field, long pid);
- size_t zmalloc_get_memory_size(void);
- void zlibc_free(void * ptr);
zmalloc
- 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));
- return ptr;#
- else * ((size_t * ) ptr) = size;
- update_zmalloc_stat_alloc(size + PREFIX_SIZE);
- return (char * ) ptr + PREFIX_SIZE;#endif
- }
分配内存大小为 size+PREFIX_SIZE 的大小,也就意味着 zmalloc 实际分配比需要多一些的内存,这一部分用于存储 size 信息。
当无法分配更多内存的时候由 zmalloc_oom_handler 来处理无法 malloc 出更多的内存
- 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;
update_zmalloc_stat_alloc 则是记录到目前为止所使用的内存大小
- #define update_zmalloc_stat_alloc(__n) do {\size_t _n = (__n);\
- if (_n & (sizeof(long) - 1)) _n += sizeof(long) - (_n & (sizeof(long) - 1));\
- if (zmalloc_thread_safe) {\atomicIncr(used_memory, __n, used_memory_mutex);\
- } else {\used_memory += _n;\
- }\
- } while ( 0 )
这里有一行很有意思,
- if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));
这一行是保证所分配的内存空间是 sizeof(long) 的整数倍,如果不是,九江这个大小扩充到这个大小。
- #ifndef HAVE_MALLOC_SIZEsize_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;
- }#endif
tcmalloc 和 jemalloc 都提供了 malloc_size 的功能可以获得 malloc 的 size,但是 glibc 的 malloc 并没有这种功能,所以 zmalloc 的做法是在头留出 PREFIX_SIZE 大小的地方用于存储大小,它只适用于封装的 malloc 方法不能方便的得到大小时所用,他会有一个 real_ptr 指向 PREFIX_SIZE 的地方,返回的确是 PREFIX_SIZE 之后的那段地址,然后你想看长度的时候就往前找 PREFIX_SIZE 的地方就行了。如果有时间我也想看看 tcmalloc 是怎么实现的。
zcalloc
calloc 与 malloc 的区别在于,malloc 并不会初始化创建出的内存区域,但是 calloc 会,malloc 的问题在于因并不会初始化导致你很难确定你创建出的这块区域中有什么东西,但 calloc 的问题也很明显,就是 calloc 会初始化这块区域也意味着他的效率要低于 malloc
- void * zcalloc(size_t size) {
- void * ptr = calloc(1, size + PREFIX_SIZE);
- if (!ptr) zmalloc_oom_handler(size);#ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_alloc(zmalloc_size(ptr));
- return ptr;#
- else * ((size_t * ) ptr) = size;
- update_zmalloc_stat_alloc(size + PREFIX_SIZE);
- return (char * ) ptr + PREFIX_SIZE;#endif
- }
代码基本与 zmalloc 相同没啥可看的
3. zrealloc
realloc 的功能是先判断当前的指针是否有足够的连续空间,如果有,扩大 mem_address 指向的地址,并且将 mem_address 返回,如果空间不够,先按照 newsize 指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来 mem_address 所指内存区域(注意:原来指针是自动释放,不需要使用 free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
- void * zrealloc(void * ptr, size_t size) {#ifndef HAVE_MALLOC_SIZE void * realptr;#endif size_t oldsize;
- void * newptr;
- if (ptr == NULL) return zmalloc(size);#ifdef HAVE_MALLOC_SIZE oldsize = zmalloc_size(ptr);
- newptr = realloc(ptr, size);
- if (!newptr) zmalloc_oom_handler(size);
- update_zmalloc_stat_free(oldsize);
- update_zmalloc_stat_alloc(zmalloc_size(newptr));
- return newptr;#
- else realptr = (char * ) ptr - PREFIX_SIZE;
- oldsize = *((size_t * ) realptr);
- newptr = realloc(realptr, size + PREFIX_SIZE);
- if (!newptr) zmalloc_oom_handler(size); * ((size_t * ) newptr) = size;
- update_zmalloc_stat_free(oldsize);
- update_zmalloc_stat_alloc(size);
- return (char * ) newptr + PREFIX_SIZE;#endif
- }
在 zmalloc 部分介绍过 zmalloc 是如何处理并得到 PREFIX_SIZE 的,如不能直接得到 size,只能往前找 PREFIX_SIZE 即大小,如无法分配 realloc 会删除原有的内存并移动到另外一块区域所以需要先用 update_zmalloc_stat_free(oldsize) 来删除之前的记录并用 update_zmalloc_stat_alloc(size) 添加新的。
zfree
- void zfree(void * ptr) {#ifndef HAVE_MALLOC_SIZE void * realptr;
- size_t oldsize;#endif
- if (ptr == NULL) return;#ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_free(zmalloc_size(ptr));
- free(ptr);#
- else realptr = (char * ) ptr - PREFIX_SIZE;
- oldsize = *((size_t * ) realptr);
- update_zmalloc_stat_free(oldsize + PREFIX_SIZE);
- free(realptr);#endif
- }
总体来说就是 zmalloc 的相反的逻辑
5. strdup
strdup 实际上就是 malloc 和 memcpy 的组合使用,只是这里用的 malloc 是 zmalloc 而已,具体哪里会用到,还要接着看以后的代码
就爱阅读 www.92to.com 网友整理上传, 为您提供最全的知识大全, 期待您的分享,转载请注明出处。
来源: