redis源代码解读之内存管理————zmalloc文件

本文章主要记录本人在看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)

该方法封装了calloc 函数功能。

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;
}

6.内存使用率

/* 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 进行的内存管理是很高效的。

对外提供的接口也比較简洁。

值得学习。

很多其它文章。欢迎关注:

时间: 2024-10-13 03:09:24

redis源代码解读之内存管理————zmalloc文件的相关文章

redis 源代码分析(一) 内存管理

一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中统一使用zmalloc,zfree一系列函数,其相应的源代码在src/zmalloc.h和src/zmalloc.c两个文件里,源代码点这里. 二,redis内存管理源代码分析 redis封装是为了屏蔽底层平台的差异,同一时候方便自己实现相关的函数,我们能够通过src/zmalloc.h 文件里的相

Linux内核源代码情景分析-内存管理

用户空间的页面有下面几种: 1.普通的用户空间页面,包括进程的代码段.数据段.堆栈段.以及动态分配的"存储堆". 2.通过系统调用mmap()映射到用户空间的已打开文件的内容. 3.进程间的共享内存区. 这些页面的的周转有两方面的意思. 1.页面的分配,使用,回收.如进程压栈时新申请的页面,这类页面不进行盘区交换,不使用时释放得以回收. 这部分通过一个场景来解释: Linux内核源代码情景分析-内存管理之用户堆栈的扩展. 2.盘区交换.如要执行硬盘上的对应代码段.把硬盘上的代码段换入内

Linux内核源代码情景分析-内存管理之用户页面的定期换出

我们已经看到在分配页面时,如果页面数不够,那么会调用page_launder,reclaim_page,__free_page将页面换出,并重新投入分配. 为了避免总是在CPU忙碌的时候,也就是在缺页异常发生的时候,临时再来搜寻可供换出的内存页面并加以换出,Linux内核定期地检查并且预先将若干页面换出,腾出空间,以减轻系统在缺页异常发生时的负担. 为此,在Linux内核中设置了一个专司定期将页面换出的"守护神"kswapd和kreclaimd. static int __init k

Linux内核源代码情景分析-内存管理之slab-回收

在上一篇文章Linux内核源代码情景分析-内存管理之slab-分配与释放,最后形成了如下图的结构: 图 1 我们看到空闲slab块占用的若干页面,不会自己释放:我们是通过kmem_cache_reap和kmem_cache_shrink来回收的.他们的区别是: 1.我们先看kmem_cache_shrink,代码如下: int kmem_cache_shrink(kmem_cache_t *cachep) { if (!cachep || in_interrupt() || !is_chaine

cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略

从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.Ref,AutoreleasePool.PoolManager Ref中包括了一个叫referenceCount的引用计数,当一个Ref类的变量被new的时候,其referenceCount的引用计数被置为1. 当中有三个重要的操作,retain.release,autorelease,以下源代码分析

arm-linux学习笔记3-linux内存管理与文件操作

配置好linux系统之后需要vim配置一下,有助于我们的编程,主要的配置如下 在/etc/vim/vimrc文件中 "显示行号 set number "自动缩进 set autoindent "智能缩进 set smartindent "一次四格 tab set tabstop=4 "一次四格 set shiftwidth=4 "括号匹配 set showmatch "右下角显示光标状态行 set ruler "文件类型检测

Magenta源代码笔记(3) —— 内存管理【转】

转自:http://blog.csdn.net/boymax2/article/details/52550197 版权声明:本文为博主原创文章,未经博主允许不得转载. Magenta内核支持虚拟地址的配置,依赖于cpu内的mmu模块. 下面会从以下几个方面对Magenta内核内存管理方面的代码进行分析: 1.mmu初始化,也就是硬件mmu的初始化,以底层寄存器操作为主,汇编 2.pmm初始化,也就是代码中物理内存结构的初始化 3.vmm初始化,也就是代码中虚拟内存结构的初始化 mmu初始化 mm

MySQL系列:innodb源代码分析之内存管理

在innodb中实现了自己的内存池系统和内存堆分配系统,在innodb的内存管理系统中,大致分为三个部分:基础的内存块分配管理.内存伙伴分配器和内存堆分配器.innodb定义和实现内存池的主要目的是提供内存的使用率和效率,防止内存碎片和内存分配跟踪和调试.我们先来看看他们的关系和结构. 下面是它的关系结构图: 上图中的: ut_mem_block块是基础内存管理 Buddy allocator是内存伙伴分配器 mem_heap是内存堆分配器 1.基础内存管理 innodb中的内存分配和内存释放是

Flink内存管理源码解读之内存管理器

回顾 上一篇文章我们谈了Flink自主内存管理的一些基础的数据结构.那篇中主要讲了数据结构的定义,这篇我们来看看那些数据结构的使用,以及内存的管理设计. 概述 这篇文章我们主要探讨Flink的内存管理类MemoryManager涉及到对内存的分配.回收,以及针对预分配内存而提供的memory segment pool.还有支持跨越多个memory segment数据访问的page view. 本文探讨的类主要位于pageckage : org.apache.flink.runtime.memor