博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
swoole之memoryGlobal内存池分析
阅读量:4957 次
发布时间:2019-06-12

本文共 4437 字,大约阅读时间需要 14 分钟。

内存池的作用:

直接使用系统调用malloc会有如下弊端:

  • 频繁分配内存时会产生大量内存碎片
  • 频繁分配内存增加系统调用开销
  • 容易造成内存泄漏
内存池是预先申请一定数量的,大小相等的内存块作为预备使用;当需要时向内存池分出一部分内存,若内存块不够使用时再向系统申请新的内存块,下面就swoole的swMemoryGlobal内存池作为分析例子

swoole swMemoryPool 数据结构设计

swMemoryGlobal是swoole内存池实现一种方式,学习内存池主要是要掌握其数据结构的设计,memoryGlobal实现如下:

// src/memory/MemoryGlobal.ctypedef struct _swMemoryPool{    void *object;                                               // 指向swMemoryGlobal指针    void* (*alloc)(struct _swMemoryPool *pool, uint32_t size);  // 分配内存函数指针    void (*free)(struct _swMemoryPool *pool, void *ptr);        // 是否内存函数指针    void (*destroy)(struct _swMemoryPool *pool);                // 销毁内存函数指针} swMemoryPool;typedef struct _swMemoryGlobal{    uint8_t shared;                          uint32_t pagesize;                    // 指定每个swMemoryGlobal_page需要申请内存大小    swLock lock;                          // 互斥锁    swMemoryGlobal_page *root_page;       // 指向第一个swMemoryGlobal_page指针,有头指针可以销毁内存池    swMemoryGlobal_page *current_page;    // 指向当前swMemoryGlobal_page指针    uint32_t current_offset;} swMemoryGlobal;typedef struct _swMemoryGlobal_page{    struct _swMemoryGlobal_page *next;    // 指向下一个节点    char memory[0];                       // 这是一个柔性数组,用于记录申请内存后的内存地址} swMemoryGlobal_page;

这三者之间的关系如下:

1504257-20181115165737882-2027821372.png

swMemoryPool

swMemoryPool可以看做是一个类,它提过了alloc,free,destory方法,以及object属性,object实际上是指向swMemoryGlobal的指针,而alloc,free,destory

则是对object操作,即通过alloc,free,destory操作swMemoryGlobal上的内容,例如:

// src/core/base.c//init global shared memorySwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1);SwooleGS = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(SwooleGS_t));

以上代码是分配sizeof(SwooleGS_t)大小内存

swMemoryGlobal

swMemoryGlobal维护着一个链表,每个节点即swMemoryGlobal_page,root_page指向第一个节点,current_page指向当前节点,pagesize指为一个节点申请

内存大小,current_offset则表示一个节点已被使用内存

swMemoryGlobal_page

swoole根据swMemoryGlobal.pagesize申请指定大小的内存,如下:

// src/memory/MemoryGlobal.cswMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm);

上面说过swMemoryGlobal_page是一个链表节点,这里需要说明的是第一个节点,第一个节点的current_offset为sizeof(swMemoryGlobal) + sizeof(swMemoryPool);

而并非为0;如下代码,当为第一个swMemoryGlobal_page申请内存后,立马就为swMemoryPool和swMemoryGlobal分配内存

// src/memory/MemoryGlobal.cgm.pagesize = pagesize;// 系统申请一个pagesize大小内存swMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm);if (page == NULL){    return NULL;}if (swMutex_create(&gm.lock, shared) < 0){    return NULL;}    gm.root_page = page;// page->memory为空闲内存gm_ptr = (swMemoryGlobal *) page->memory;gm.current_offset += sizeof(swMemoryGlobal);// swMemoryPool指向空闲内存偏移地址,占用sizeof(swMemoryPool)内存swMemoryPool *allocator = (swMemoryPool *) (page->memory + gm.current_offset);gm.current_offset += sizeof(swMemoryPool);allocator->object = gm_ptr;allocator->alloc = swMemoryGlobal_alloc;allocator->destroy = swMemoryGlobal_destroy;allocator->free = swMemoryGlobal_free;// 将gm写入到gm_ptr,即空闲内存前sizeof(gm)用于swMemoryGlobalmemcpy(gm_ptr, &gm, sizeof(gm));

分配内存

分配内存由swMemoryGlobal_alloc方法执行;该方法为swMemoryPool一个函数指针,如下

allocator->alloc = swMemoryGlobal_alloc;        // 分配方法
// src/core/base.c//init global shared memorySwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1);SwooleGS = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(SwooleGS_t));// src/memory/MemoryGlobal.cstatic void *swMemoryGlobal_alloc(swMemoryPool *pool, uint32_t size){    swMemoryGlobal *gm = pool->object;    gm->lock.lock(&gm->lock);    if (size > gm->pagesize - sizeof(swMemoryGlobal_page)) // sizeof(swMemoryGlobal_page)为swMemoryGlobal_page类型的指针大小    {        swWarn("failed to alloc %d bytes, exceed the maximum size[%d].", size, gm->pagesize - (int) sizeof(swMemoryGlobal_page));        gm->lock.unlock(&gm->lock);        return NULL;    }    // 如果一个节点不够分配内存,则重新申请一个新节点,并设置当前节点current_page为新节点    if (gm->current_offset + size > gm->pagesize - sizeof(swMemoryGlobal_page))    {        swMemoryGlobal_page *page = swMemoryGlobal_new_page(gm);        if (page == NULL)        {            swWarn("swMemoryGlobal_alloc alloc memory error.");            gm->lock.unlock(&gm->lock);            return NULL;        }        gm->current_page = page;    }    void *mem = gm->current_page->memory + gm->current_offset;    gm->current_offset += size;    gm->lock.unlock(&gm->lock);        // 结果返回空闲内存的偏移地址    return mem;}

柔性数组

柔性数组(0长度数组)作用: 为了满足需要变长度的结构体(结构体是可变长的)

  • 数组名不占用空间,分配的内存是连续的
  • 不会像定长数组一样浪费空间
  • 不会像指针一样需要分别分配内存,分别释放内存

定长数组使用方便, 但是却浪费空间, 指针形式只多使用了一个指针的空间, 不会造成

原文地址:

转载于:https://www.cnblogs.com/lalalagq/p/9964608.html

你可能感兴趣的文章
[置顶] 一名优秀的程序设计师是如何管理知识的?
查看>>
关于使用“状态模式”做工作流概要。
查看>>
谈谈:程序集加载和反射
查看>>
mysql主从复制(超简单)
查看>>
scanf和gets
查看>>
highcharts 图表实例
查看>>
定时器使用
查看>>
LeetCode Median of Two Sorted Arrays
查看>>
【知识强化】第二章 线性表 2.2 线性表的顺序表示
查看>>
19.30内置登录处理
查看>>
00_前情回顾
查看>>
fortran90简明教程
查看>>
flex知识点归纳
查看>>
hdu 5442 Favorite Donut 最大表示法+KMP
查看>>
ubuntu下如何查看用户登录及系统授权相关信息
查看>>
丶制作一个数字猜猜看小游戏
查看>>
秋季学期学习总结
查看>>
SpringBoot 优化内嵌的Tomcat
查看>>
Dagger2 入门解析
查看>>
【LaTeX】E喵的LaTeX新手入门教程(1)准备篇
查看>>