本文章主要记录本人在看redis源码的一些理解和想法。因为功力有限,肯定会出现问题,所以,希望高手给出指正。
第一篇就是内存相关的介绍。因为我喜欢先看一些组件的东西,再看整体的流程。先上一下代码吧
头文件
- //主要提供内存分配和释放的基础功能
- 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(void);
- size_t zmalloc_get_rss(void);
- size_t zmalloc_get_private_dirty(void);
- void zlibc_free(void *ptr);
- #ifndef HAVE_MALLOC_SIZE
- size_t zmalloc_size(void *ptr);
- #endif
0.前言
在这块代码我们可以看到HAVE_ATOMIC 宏定义,有什么作用呢,
该文件主要提供了tcmalloc 和jemalloc内存的管理。
tcmalloc是google perftool的一部分,与一般的内存池不同,它直接与os打交道,内存闲置时os会进行回收(stl内存池就不回收),同时使用TLS(Thread local storage)管理内存池,避免一个线程内分配内存都要同步。
jemalloc与tcmalloc相似,作者Jason Evans是Free BSD开发人员,性能与使用率与tcmalloc不相伯仲。tcmalloc更方便与google perftool集成,进行性能评测。
zmalloc主要是 提供了对malloc函数的封装,如果是glibc的malloc函数,那么分配的内存是长度+要分配的的内存,然后将头部放入大小
1.申请内存
- 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
- }
- update_zmalloc_stat_alloc(zmalloc_size(ptr));是先将自己内存对齐,如果long是4位就对齐到4的整数倍。然后将内存的大小记录下来到一个全局变量中
- 如下代码
- #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) { \
- update_zmalloc_stat_add(_n); \
- } else { \
- used_memory += _n; \
- } \
- } while(0)
内存对其有很多种方式,他这种也是很有意思。使用long就可以不管是64机器还是32位机器了。如果是在多线程情况下是使用了互斥锁。
如果没定义宏HAVE_MALLOC_SIZE,就在内存的前边四个字节存储器大小。最后返回内存的起始地址。
同理
- void *zcalloc(size_t size)
2.重新申请内存
代码如下
- 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
- }
该方法就是对指定的一段内存重新申请指定大小的内存。地址没变化。
3.释放内存
- 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
- }
4.拷贝内存,封装了memcpy函数
- char *zstrdup(const char *s) {
- size_t l = strlen(s)+1;
- char *p = zmalloc(l);
- memcpy(p,s,l);
- return p;
- }
5获取RSS 物理内存值
通过查看进程的/proc/pid/stat 文件的第23行。
- size_t zmalloc_get_rss(void) {
- int page = sysconf(_SC_PAGESIZE);
- size_t rss;
- char buf[4096];
- char filename[256];
- int fd, count;
- char *p, *x;
- snprintf(filename,256,"/proc/%d/stat",getpid());
- if ((fd = open(filename,O_RDONLY)) == -1) return 0;
- if (read(fd,buf,4096) <= 0) {
- close(fd);
- return 0;
- }
- close(fd);
- p = buf;
- count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
- while(p && count--) {
- p = strchr(p,' ');
- if (p) p++;
- }
- if (!p) return 0;
- x = strchr(p,' ');
- if (!x) return 0;
- *x = '\0';
- rss = strtoll(p,NULL,10);
- rss *= page;
- return rss;
- }
- /* Fragmentation = RSS / allocated-bytes */
- float zmalloc_get_fragmentation_ratio(void) {
- return (float)zmalloc_get_rss()/zmalloc_used_memory();
- }
7.多线程下的原子增加
- #ifdef HAVE_ATOMIC
- #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))
- #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))
- #else
- #define update_zmalloc_stat_add(__n) do { \
- pthread_mutex_lock(&used_memory_mutex); \
- used_memory += (__n); \
- pthread_mutex_unlock(&used_memory_mutex); \
- } while(0)
- #define update_zmalloc_stat_sub(__n) do { \
- pthread_mutex_lock(&used_memory_mutex); \
- used_memory -= (__n); \
- pthread_mutex_unlock(&used_memory_mutex); \
- } while(0)
- #endif
redis这块内存使用谷歌和jemalloc 进行的内存管理是非常高效的。
对外提供的接口也比较简洁。