一、內存池概述
- 服務器會頻繁地對內存進行申請和釋放,頻繁地操作將帶來如下的缺點:
- 服務器性能降低,因爲需要頻繁地申請和釋放內存
- 內存碎片化多
- 等等......
- 市場上開源的比較好用的內存池有:
- tcmalloc:MySQL使用的
- jemalloc:Tomcat使用的
- Nginx也有一套自己的內存池(本文就是仿照Nginx的內存池設計的)
二、內存池設計
- 因爲內存頁的大小爲4K,因此在設計內存池的時候,我們將認爲:
- <4k的內存:稱爲小內存塊
- >4K的內存:稱爲大內存快
struct mp_large_s(大內存塊結構)
- 當我們申請的內存大於4K時,就會申請一個大的內存塊,這個結構體就是大內存結構
- 該結構不是真正存儲內存的,真正的內存是由其alloc成員所指向的
- 如上結構圖所示(藍色部分):
- alloc:指向真正的大內存塊地址
- next:指向下一個struct mp_large_s結構的指針
//大內存塊節點
struct mp_large_s {
void *alloc; //指向該大內存快的實際存儲區域
struct mp_large_s *next; //指向下一塊大內存快
};
struct mp_node_s(大內存塊結構)
- 當我們申請的內存小於4K時,就會申請一個小的內存塊,這個結構體就是小內存結構
- 與struct mp_large_s結構一樣,該結構不是真正存儲內存的,真正的內存是在結構後面的data區域存儲的
- 如上結構圖所示(藍色部分):
- last:指向後面data區域中可用內存的起始地址
- end:指向後面data區域最後的指針
- next:指向下一個struct mp_node_s結構的指針
- failed:表示當前內存塊是否停止使用
//小內存塊節點
struct mp_node_s {
unsigned char *last; //後面數據存儲區域中可用區域的起點
unsigned char *end; //後面數據存儲區域的終點
struct mp_node_s *next; //指向下一塊小內存塊
int failed; //該內存塊是否不再使用
};
struct mp_pool_s(內存池)
- 該結構是內存池的真正結構,我們操作內存池時就是操作這個結構
- 如上結構圖所示(藍色部分):
- max:區別大內存塊與小內存塊的界值,上面我們提到過了,我們本案例以4K爲例
- current:指向小內存塊鏈表的起點
- large:指向大內存塊鏈表的起點
- head[0]:柔性數組,struct mp_node_s head[0]相當於struct mp_node_s *head。用來指向於current鏈表中的第一個struct mp_node_s節點,通過這個成員可以方便的操作current鏈表
//內存池,管理者所有的內存塊
struct mp_pool_s {
//區別大內存塊與小內存塊的界值: <max的用struct mp_node_s表示, >max的用struct mp_large_s表示
int max;
struct mp_node_s *current; //管理小內存塊的鏈表起點
struct mp_large_s *large; //管理大內存快的鏈表起點
//柔型數組: head[0]指向current鏈表第1個節點, head[1]指向current鏈表第2個節點...以此類推
//用這個數組來操作每個mp_node_s節點相關指標(比如更改last等),不需要再去遍歷
struct mp_node_s head[0];
};
三、內存池接口設計
四、代碼實現
五、運行效果