高并發內存池(1)-定長內存池
可以采用兩種方式:
方式1:
template <size_t N>
方式2:
template <class T>
獲取到T對象大小的內存池,更推薦使用方式二,因為可以動態靈活調整類型
需要的成員變量:
_memory:表示一大塊內存,需要確定使用的變量類型
能否使用void*?
*表示的是指針,指針代表的是地址,它的指向需要有意義, *的前面表示的是類型,void * 沒有意義,既不能解引用又不能進行加減
解決方案就是換成char*,因為一個char表示一個字節,也可以用int之類的,但是不會方便,比如要是取3字節啥的
怎么管理需要還回來的鏈表?
用自由鏈表
怎么樣去連接?
不用結構體,用其內存塊當節點,用頭四個或者八個節點存儲下一個位置的地址,這時候需要處理如果只剩最后一個會不會越界的情況,這時候我們需要引入新的成員變量remianBytes來知道剩余內存的大小,
自由鏈表需要進行頭插,注意類型是void*,因為void *在32位下是4字節,64位是八字節,不用寫if else條件判斷語句去判斷
整體的代碼如下:
#pragma once#include <iostream>
#include <vector>
#include <time.h>using std::cout;
using std::endl;#ifdef _WIN32
#include <windows.h>
#else
// ...
#endif// 直接去堆上按頁申請空間
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}//定長內存池:每次獲取到T對象大小的內存池
template <class T>
class ObjectPool
{public://對內存進行分配T* New(){//創建每次需要分配的內存塊T* obj =nullptr;//優先使用自由鏈表里面的if (_freeList){//定義next指針void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;}//如果自由鏈表里面的不夠了,再進行創建else{// 剩余內存不夠一個對象大小時,則重新開大塊空間if (remainBytes < sizeof(T)){//初始化remainBytes的大小remainBytes = 128 * 1024;//空間開辟大小,右移13相當于除以2的十三次方,2的10次方已經是1000多_memory = (char*)SystemAlloc(_remainBytes >> 13);//如果開創空間有異常就拋出異常if (_memory == nullptr){throw std::bad_alloc();}}//此時創建空間obj = (T*)_memory;//判斷T對象的大小,void*在32位下是4字節,64位下是8字節size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);//切下空間_memory += objSize;//剩余空間去扣除相應的大小remainBytes -= objSize;}// 定位new,顯示調用T的構造函數初始化,以防會調用如vector,string之類的new(obj)T();return obj;}//自由鏈表進行管理T* Delete(T*obj){// 顯示調用析構函數清理對象obj->~T();// 頭插,存前4個或者8個字節到下一節點*(void**)obj = _freeList;_freeList = obj;}private:char* _memory = nullptr;// 指向大塊內存的指針size_t remainBytes = 0;// 大塊內存在切分過程中剩余字節數void* _freeList = nullptr;// 還回來過程中鏈接的自由鏈表的頭指針
};
整體的流程:先看freeList有沒有空余的,先進行頭刪,然后再到_memory里面去切,如果沒有多余的,就去找系統申請大塊內存
如何釋放?不需要釋放,不需要擔心內存是否會泄漏,因為只要進程結束,它也能正常釋放