### 內存池的作用:
直接使用系統調用malloc會有如下弊端:
- 頻繁分配內存時會產生大量內存碎片
- 頻繁分配內存增加系統調用開銷
- 容易造成內存泄漏
> 內存池是預先申請一定數量的,大小相等的內存塊作為預備使用;當需要時向內存池分出一部分內存,若內存塊不夠使用時再向系統申請新的內存塊,下面就swoole的swMemoryGlobal內存池作為分析例子
### swoole swMemoryPool 數據結構設計
swMemoryGlobal是swoole內存池實現一種方式,學習內存池主要是要掌握其數據結構的設計,memoryGlobal實現如下:
```c
// src/memory/MemoryGlobal.c
typedef 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;
```
這三者之間的關系如下:

### swMemoryPool
swMemoryPool可以看做是一個類,它提過了alloc,free,destory方法,以及object屬性,object實際上是指向swMemoryGlobal的指針,而alloc,free,destory
則是對object操作,即通過alloc,free,destory操作swMemoryGlobal上的內容,例如:
```c
// src/core/base.c
//init global shared memory
SwooleG.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申請指定大小的內存,如下:
```c
// src/memory/MemoryGlobal.c
swMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm);
```
上面說過swMemoryGlobal_page是一個鏈表節點,這里需要說明的是第一個節點,第一個節點的current_offset為sizeof(swMemoryGlobal) + sizeof(swMemoryPool);
而并非為0;如下代碼,當為第一個swMemoryGlobal_page申請內存后,立馬就為swMemoryPool和swMemoryGlobal分配內存
```c
// src/memory/MemoryGlobal.c
gm.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)用于swMemoryGlobal
memcpy(gm_ptr, &gm, sizeof(gm));
```
### 分配內存
分配內存由swMemoryGlobal_alloc方法執行;該方法為swMemoryPool一個函數指針,如下
```c
allocator->alloc = swMemoryGlobal_alloc; // 分配方法
```
```c
// src/core/base.c
//init global shared memory
SwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1);
SwooleGS = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(SwooleGS_t));
// src/memory/MemoryGlobal.c
static 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長度數組)作用: 為了滿足需要變長度的結構體(結構體是可變長的)
- 數組名不占用空間,分配的內存是連續的
- 不會像定長數組一樣浪費空間
- 不會像指針一樣需要分別分配內存,分別釋放內存
定長數組使用方便, 但是卻浪費空間, 指針形式只多使用了一個指針的空間, 不會造成
[柔性數組參考](http://www.360doc.com/content/17/1119/10/9200790_705214752.shtml)
- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配