基于一個(gè)簡(jiǎn)單定長(zhǎng)內(nèi)存池的實(shí)現(xiàn)方法詳解
主要分為 3 個(gè)部分,memoryPool 是管理內(nèi)存池類,block 表示內(nèi)存塊,chunk 表示每個(gè)存儲(chǔ)小塊。它們之間的關(guān)系為,memoryPool 中有一個(gè)指針指向某一起始 block,block 之前通過(guò) next 指針構(gòu)成鏈表結(jié)構(gòu)的連接,每個(gè) block 包含指定數(shù)量的 chunk。每次分配內(nèi)存的時(shí)候,分配 chunk 中的數(shù)據(jù)地址。
主要數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì):
Block:
struct block {
block * next;//指向下一個(gè)block指針
unsigned int numofChunks;
unsigned int numofFreeChunks;//剩余free的chunk數(shù)量
unsigned int blockNum;//該block的編號(hào)
char * data;
queue<int> freepos; //記錄可用chunk序號(hào)
};
MemoryPool:
class memoryPool {
unsigned int initNumofChunks; //每個(gè)block的chunk數(shù)量
unsigned int chunkSize;//每個(gè)chunk的數(shù)據(jù)大小
unsigned int steps;//每次擴(kuò)展的chunk數(shù)量
unsigned int numofBlocks;//當(dāng)前管理多少個(gè)blocks
block * blocksPtr;//指向起始block
block * blocksPtrTail;//指向末尾block
block * firstHasFreeChunksBlock;//指向第一個(gè)不為空的block
};
Chunk:
ChunkNum:該 chunk 所在 block 里的編號(hào)
blockAddress: 該 chunk 所對(duì)應(yīng)的 block,主要用于 free 這個(gè) chunk 的時(shí)候,能夠快速找到所屬 block,并進(jìn)行相應(yīng)更新
data:實(shí)際供使用的數(shù)據(jù)起始位置
關(guān)鍵操作說(shuō)明:
內(nèi)存分配:
從 firstHasFreeChunksBlock 開始查找第一個(gè)有 free 位置的 block,如果找到,則則獲取該 block 的 freepos 的隊(duì)首元素,返回該元素序號(hào)對(duì)應(yīng)的 chunk 的數(shù)據(jù)地址,并將 freepos 的隊(duì)首元素彈出,其他相關(guān)屬性更新。如果找不到,則新增 steps 個(gè) chunk,再重復(fù)上面的過(guò)程。
內(nèi)存釋放:
傳入待釋放的地址指針p,通過(guò)對(duì)p的地址移動(dòng)可以找到chunk中的 ChunkNum 和 blockAddress 兩個(gè)數(shù)據(jù),通過(guò) blockAddress 可以找到該 chunk 所屬的 block,然后將ChunkNum 添加到該 block 的 freepos 中,其他相應(yīng)屬性更新。
使用方法:
memoryPool * mp = new memoryPool (256);
char * s = (char *)mp->allocate();
// 一些操作
mp->freeMemory(s);
delete mp;
不足:
沒考慮線程安全問題,該實(shí)現(xiàn)方案在單線程下可以正常運(yùn)行。
程序源代碼:
#include <iostream>
#include <queue>
#include <string.h>
#include <ctime>
using namespace std;
struct block {
block * next;
unsigned int numofChunks;//指向下一個(gè)block指針
unsigned int numofFreeChunks;//剩余free的chunk數(shù)量
unsigned int blockNum;//該block的編號(hào)
char * data;
//記錄可用chunk序號(hào)
queue<int> freepos;
block(unsigned int _numofChunks ,unsigned int _chunkSize, unsigned int _blockNum){
numofChunks = _numofChunks;
numofFreeChunks = _numofChunks;
blockNum = _blockNum;
next = NULL;
data = new char [numofChunks * (sizeof(unsigned int) + sizeof(void *) + _chunkSize)];
char * p = data;
//每個(gè)chunk的結(jié)構(gòu):4byte的chunk序號(hào) + 4byte的所屬block地址 + 真正的數(shù)據(jù)
for(int i=0;i<numofChunks;i++){
char * ptr = p + i * (_chunkSize + sizeof(unsigned int) + sizeof(void *));
unsigned int * num = (unsigned int *)(ptr);
*num = i;
ptr += sizeof(void *);
int * blockpos = (int *) ptr;
*blockpos = (int)this;
freepos.push(i);
}
}
~block(){
delete [] data;
}
};
class memoryPool {
public :
memoryPool(unsigned int _chunkSize = 256, unsigned int _initNumofChunks = 4096, unsigned int _steps = 64){
initNumofChunks = _initNumofChunks;
chunkSize = _chunkSize;
steps = _steps;
numofBlocks = steps;
//創(chuàng)建內(nèi)存池時(shí),初始化一定數(shù)量的內(nèi)存空間
block * p = new block(initNumofChunks, chunkSize, 0);
blocksPtr = p;
for(int i=1;i<steps;i++){
p->next = new block(initNumofChunks, chunkSize, i);
p = p->next;
blocksPtrTail = p;
}
firstHasFreeChunksBlock = blocksPtr;
}
~memoryPool(){
block * p = blocksPtr;
while(blocksPtr!=NULL){
p = blocksPtr->next;
delete blocksPtr;
blocksPtr = p;
}
}
/*
從firstHasFreeChunksBlock開始查找第一個(gè)有free位置的block,
如果找到,則則獲取該block的freepos的對(duì)首元素,
返回該元素序號(hào)對(duì)應(yīng)的chunk的數(shù)據(jù)地址,并將freepos的隊(duì)首元素彈出,
其他相關(guān)屬性更新。如果找不到,則新增steps個(gè)chunk,再重復(fù)上面的過(guò)程。
*/
void * allocate(){
block * p = firstHasFreeChunksBlock;
while(p != NULL && p->numofFreeChunks <= 0) p = p->next;
if(p == NULL){
p = blocksPtrTail;
increaseBlocks();
p = p->next;
firstHasFreeChunksBlock = p;
}
unsigned int pos = p->freepos.front();
void * chunkStart = (void *)(p->data + pos * (chunkSize + sizeof(unsigned int) + sizeof(void *)));
void * res = chunkStart + sizeof(unsigned int) + sizeof(void *);
p->freepos.pop();
p->numofFreeChunks --;
return res;
}
void increaseBlocks(){
block * p = blocksPtrTail;
for(int i=0; i<steps; i++){
p->next = new block(initNumofChunks, chunkSize, numofBlocks);
numofBlocks++;
p = p->next;
blocksPtrTail = p;
}
}
/*
傳入待釋放的地址指針_data,
通過(guò)對(duì)_data的地址移動(dòng)可以找到chunk中的ChunkNum和blockAddress兩個(gè)數(shù)據(jù),
通過(guò)blockAddress可以找到該chunk所屬的block,
然后將ChunkNum添加到該block的freepos中,其他相應(yīng)屬性更新。
*/
void freeMemory(void * _data){
void * p = _data;
p -= sizeof(void *);
int * blockpos = (int *) p;
block * b = (block *) (*blockpos);
p -= sizeof(unsigned int);
int * num = (int *) p;
b->freepos.push(*num);
b->numofFreeChunks ++;
if (b->numofFreeChunks > 0 && b->blockNum < firstHasFreeChunksBlock->blockNum)
firstHasFreeChunksBlock = b;
}
private :
unsigned int initNumofChunks; //每個(gè)block的chunk數(shù)量
unsigned int chunkSize;//每個(gè)chunk的數(shù)據(jù)大小
unsigned int steps;//每次擴(kuò)展的chunk數(shù)量
unsigned int numofBlocks;//當(dāng)前管理多少個(gè)blocks
block * blocksPtr;//指向起始block
block * blocksPtrTail;//指向末尾block
block * firstHasFreeChunksBlock;//指向第一個(gè)不為空的block
};
//test
void echoPositionNum(char * p){
p -= (sizeof(void *) + sizeof(unsigned int));
int * num = (int *) p;
cout<<*num<<endl;
}
//測(cè)試
void test0(){
memoryPool mp;
char * s1 = (char *)mp.allocate();
char * s2 = (char *)mp.allocate();
char str [256];
char str2 [256];
char str3 [256];
for(int i=0; i<255; i++) {
str[i] = 'a';str2[i] = 'b';str3[i] = 'c';
}
str[255] = '\0';
str2[255] = '\0';
strcpy(s1,str);
strcpy(s2,str2);
str3[255] = '\0';
echoPositionNum(s1);
cout<<s1<<endl;
mp.freeMemory(s1);
echoPositionNum(s2);
cout<<s2<<endl;
char * s3 = (char *)mp.allocate();
strcpy(s3,str3);
echoPositionNum(s3);
cout<<s3<<endl;
}
void test1(){
clock_t clock_begin = clock();
const int N = 50000;
char * s[N];
int round = 100;
while(round>=0){
round --;
for(int i=0;i<N;i++){
s[i] = new char[256];
}
for(int i=0;i<N;i++){
delete [] s[i];
}
}
clock_t clock_end = clock();
cout<<"Time cost\t"<<clock_end - clock_begin<<endl;
}
void test2(){
memoryPool mp(256);
clock_t clock_begin = clock();
const int N = 50000;
char * s[N];
int round = 100;
while(round>=0){
round --;
for(int i=0;i<N;i++){
s[i] = (char *)mp.allocate();
}
for(int i=0;i<N;i++){
mp.freeMemory(s[i]);
}
}
clock_t clock_end = clock();
cout<<"Time cost\t"<<clock_end - clock_begin<<endl;
}
int main()
{
test0();
test1();
test2();
return 0;
}
運(yùn)行結(jié)果:
相關(guān)文章
C語(yǔ)言 深入解讀數(shù)據(jù)結(jié)構(gòu)之堆的實(shí)現(xiàn)
堆就是用數(shù)組實(shí)現(xiàn)的二叉樹,所以它沒有使用父指針或者子指針。堆根據(jù)“堆屬性”來(lái)排序,“堆屬性”決定了樹中節(jié)點(diǎn)的位置2021-11-11C語(yǔ)言中字符的輸入輸出以及計(jì)算字符個(gè)數(shù)的方法詳解
這篇文章主要介紹了C語(yǔ)言中字符的輸入輸出以及計(jì)算字符個(gè)數(shù)的方法,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11C語(yǔ)言采用文本方式和二進(jìn)制方式打開文件的區(qū)別分析
這篇文章主要介紹了C語(yǔ)言采用文本方式和二進(jìn)制方式打開文件的區(qū)別分析,有助于讀者更好的理解文本文件與二進(jìn)制文件的原理,需要的朋友可以參考下2014-07-07