haproxy-代码阅读-内存管理

简介: haproxy内存池概述内存池按照类型分类,每个类型的内存池都有一个名字,用链表记录空闲的内存块,每个内存块大小相等,并按照16字节对齐。haporxy用pool_head 结构记录内存池struct pool_head { void **free_list; /* 空闲链表 */...

haproxy内存池概述

内存池按照类型分类,每个类型的内存池都有一个名字,用链表记录空闲的内存块,每个内存块大小相等,并按照16字节对齐。
haporxy用pool_head 结构记录内存池

struct pool_head {
    void **free_list;   /* 空闲链表 */
    struct list list;   /* 双向链表,链接每种类型的内存池 */
    unsigned int used;  /* 使用了多少内存块 */
    unsigned int allocated; /* 分配了多少内存块 */
    unsigned int limit; /* 内存块上限 */
    unsigned int minavail;  /* 最少保留几个,回收时不会全部回收 */
    unsigned int size;  /* 内存块大小 */
    unsigned int flags; /* 能否共享,类型不同,但大小相同的,能否共享一个pool_head */
    unsigned int users; /* 内存池有几个使用者 */
    char name[12];      /* 内存池名称 */
};

在程序执行过程中,产生的内存池,很有可能按照大小,排列成如下方式:
img_b4b0b975d6269bd5990b328347b493d1.png

内存池的创建

haproxy创建内存池时,会先检查内存池中,有没有与所需大小相同的内存池,有且内存池可共享,将pool_head.users++。若没有,则新创建一个内存池。

struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
{
    struct pool_head *pool;
    struct pool_head *entry;
    struct list *start;
    unsigned int align;
    
    //按照16字节对齐
    align = 16;
    size  = (size + align - 1) & -align;
    //pools是全局变量,内存池的头节点
    start = &pools;
    pool = NULL;

    
    list_for_each_entry(entry, &pools, list) {
        if (entry->size == size) {
            if (flags & entry->flags & MEM_F_SHARED) {//大小相等且可共享
                pool = entry;
                break;
            }
        }
        else if (entry->size > size) { //内存池按照大小排序,新pool_head,插在适当位置
            start = &entry->list;
            break;
        }
    }
    //创建一个新的内存池
    if (!pool) {
        pool = CALLOC(1, sizeof(*pool));
        if (!pool)
            return NULL;
        if (name)
            strlcpy2(pool->name, name, sizeof(pool->name));
        pool->size = size;
        pool->flags = flags;
        LIST_ADDQ(start, &pool->list);
    }
    pool->users++;
    return pool;
}

内存申请

create_pool仅仅是申请了内存池的类型,还没有具体分配内存,分配内存的工作由pool_refill_alloc来完成

void *pool_refill_alloc(struct pool_head *pool)
{
    void *ret;

    //如果可申请的内存块有上限,且已达上限,不再申请
    if (pool->limit && (pool->allocated >= pool->limit))
        return NULL;
    ret = MALLOC(pool->size);
    //如果申请失败,pool_gc2()垃圾回收,然后再申请一次,再失败就放弃
    if (!ret) {
        pool_gc2(); 
        ret = MALLOC(pool->size);
        if (!ret)
            return NULL;
    }
    pool->allocated++;
    pool->used++;
    return ret;
}

其中,pool_gc2()垃圾回收函数,会遍历所有内存池,并释放空闲内存块(留下minavail的数量)。
使用者申请内存,不是直接调用pool_refill_alloc,而是通过调用pool_alloc2,如果free_list中没有空闲内存了,则调用pool_refill_alloc申请下内存。如果还有空闲内存,则使用第一块内存,free_list指向下一块。

#define pool_alloc2(pool)                                     \
({                                                            \
        void *__p;                                            \
        if ((__p = pool->free_list) == NULL)                  \
                __p = pool_refill_alloc(pool);                \
        else {                                                \
                pool->free_list = *(void **)pool->free_list;  \
                pool->used++;                                 \
        }                                                     \
        __p;                                                  \
})

内存释放

如果内存块的使用者,在申请内存块后,不主动释放内存块,那么销毁内存池后,内存块将无法回到内存。所以,必须注意,要主动释放内存块。
内存块的释放很简单,将内存块直接放到空闲链表的第一个节点就行。

#define pool_free2(pool, ptr)                           \
({                                                      \
        *(void **)ptr = (void *)pool->free_list;        \
        pool->free_list = (void *)ptr;                  \
        pool->used--;                                   \
        pool_gc2_ifneed(pool);                          \
})

销毁内存池

内存池的销毁很简单,释放所有空闲内存块,然后释放内存池。如果还有使用中的内存(pool->used != 0),停止释放

void *pool_destroy2(struct pool_head *pool)
{
    if (pool) {
        pool_flush2(pool);
        if (pool->used)
            return pool;
        pool->users--;
        if (!pool->users) {
            LIST_DEL(&pool->list);
            FREE(pool);
        }
    }
    return NULL;
}

其中, pool_flush2函数会直接释放掉所有空闲内存

void pool_flush2(struct pool_head *pool)
{
    void *temp, *next;
    if (!pool)
        return;

    next = pool->free_list;
    while (next) {
        temp = next;
        next = *(void **)temp;
        pool->allocated--;
        FREE(temp);
    }
    pool->free_list = next;
}
目录
相关文章
|
3月前
|
存储 缓存 监控
|
6月前
|
监控 算法 Java
如何优化Java应用程序的内存管理
如何优化Java应用程序的内存管理
|
6月前
|
监控 算法 Java
深入理解Java虚拟机:内存管理与性能优化
在Java的世界里,虚拟机(JVM)是幕后的守护者,它默默地支撑着每一个字节码的运行。本文将揭开JVM的神秘面纱,探讨其内存管理机制及如何通过调优提升应用性能。从堆内存的分配到垃圾回收的策略,再到实践中的性能监控与调优技巧,我们将一同走进JVM的内部世界,学习如何让我们的Java程序跑得更快、更稳。
|
8月前
|
缓存 PHP 数据库
PHP程序性能优化指南
在当今互联网快速发展的时代,PHP作为一种流行的服务器端脚本语言,其性能优化显得尤为重要。本文将介绍一些提升PHP程序性能的有效方法,帮助开发者更好地优化他们的代码,提升应用程序的响应速度和效率。
|
存储 Unix Shell
软件运行机制及内存管理
软件运行机制及内存管理
192 0
聊一聊内存管理(二)
聊一聊内存管理(二)
77 0
|
算法
聊一聊内存管理(一)
聊一聊内存管理(一)
93 0
|
运维 监控 Linux
学点Linux命令没坏处(进程管理)
操作系统中每个软件的运行都是相当于开启了一个或多个进程,在window中的任务管理器可以清晰的看到我们正在运行的那些进程并且可以通过鼠标操作结束或调整进程,而在linux我们需要使用命令来进行这一系列操作。本文主要介绍下Linux常用的进程管理命令,主要从启动进程=》查看进程=》修改进程=》结束进程这几个方面来处理。
|
负载均衡 网络协议 算法
haproxy是干什么的?底层原理是什么?
haproxy是干什么的?底层原理是什么?
439 0
|
存储 编译器 程序员
C与C++内存管理避坑指南
C与C++内存管理避坑指南
137 0