C++設(shè)計一個簡單內(nèi)存池的全過程
什么是內(nèi)存池???
通常我們用new或malloc來分配內(nèi)存的話,由于申請的大小不確定,所以當(dāng)頻繁的使用時會造成內(nèi)存碎片和效率的降低。為了克服這種問題我們提出了內(nèi)存池的概念。內(nèi)存池是一種內(nèi)存分配方式。內(nèi)存池的優(yōu)點就是可以有效的減少內(nèi)存碎片化,分配內(nèi)存更快速,減少內(nèi)存泄漏等優(yōu)點。
內(nèi)存池是在真正使用內(nèi)存之前,先申請分配一個大的內(nèi)存塊留作備用。當(dāng)真正需要使用內(nèi)存的時候,就從內(nèi)存池中分配一塊內(nèi)存使用,當(dāng)使這塊用完了之后再還給內(nèi)存池。若是內(nèi)存塊不夠了就向內(nèi)存再申請一塊大的內(nèi)存塊。
可以看出這樣做有兩個好處:
1、由于向內(nèi)存申請的內(nèi)存塊都是比較大的,所以能夠降低外碎片問題。
2、一次性向內(nèi)存申請一塊大的內(nèi)存慢慢使用,避免了頻繁的向內(nèi)存請求內(nèi)存操作,提高內(nèi)存分配的效率。
內(nèi)存碎片化:
造成堆利用率很低的一個主要原因就是內(nèi)存碎片化。如果有未使用的存儲器,但是這塊存儲器不能用來滿足分配的請求,這時候就會產(chǎn)生內(nèi)存碎片化問題。內(nèi)存碎片化分為內(nèi)部碎片和外部碎片。
內(nèi)碎片:
內(nèi)部碎片是指一個已分配的塊比有效載荷大時發(fā)生的。(舉個栗子:假設(shè)以前分配了10個大小的字節(jié),現(xiàn)在只用了5個字節(jié),則剩下的5個字節(jié)就會內(nèi)碎片)。內(nèi)部碎片的大小就是已經(jīng)分配的塊的大小和他們的有效載荷之差的和。因此內(nèi)部碎片取決于以前請求內(nèi)存的模式和分配器實現(xiàn)的模式。

外碎片: 外部碎片就是當(dāng)空閑的存儲器的和計起來足夠滿足一個分配請求,但是沒有一個單獨的空閑塊足夠大可以處理這個請求。外部碎片取決于以前的請求內(nèi)存的模式和分配器的實現(xiàn)模式,還取決于于將來的內(nèi)存請求模式。所以外部碎片難以量化。

下面介紹一種簡單的內(nèi)存池,它是針對于某種對象實現(xiàn)的。 我們可以用一個鏈表實現(xiàn)這個內(nèi)存池,鏈表上的每個結(jié)點都是一個對象池,如果我們需要申請空間的話,直接去內(nèi)存池里面申請空間,當(dāng)用完之后再還給內(nèi)存池。

內(nèi)存池的設(shè)計主要包含三步:
1、初始化
在創(chuàng)建內(nèi)存池的時候為內(nèi)存池分配了一塊很大的內(nèi)存,便于以后的使用。
2、分配內(nèi)存
當(dāng)需要內(nèi)存的時候就去內(nèi)存池里面分配內(nèi)存。
3、回收內(nèi)存
當(dāng)從內(nèi)存池里面分配來的內(nèi)存使用完畢之后,需要將這塊內(nèi)存還給內(nèi)存池。
設(shè)計上面這個內(nèi)存池最重要的問題就是如何重復(fù)利用釋放回來的內(nèi)存,讓利用率達到最高???

但是如果當(dāng)對象的大小小于對象指針的時候,也就是一個對象的空間存不下一個指針的大小,這時候就不可避免的產(chǎn)生內(nèi)碎片。 例如:為T類型對象開辟對象池,sizeof(T)<sizeof(T*),這時候我們就要為一個T類型對象申請sizeof(T*)大小的內(nèi)存。
代碼實現(xiàn):
#pragma once
#include<iostream>
using namespace std;
//用鏈表來實現(xiàn)內(nèi)存池,每一個結(jié)點都掛有一塊內(nèi)存
template<typename T>
class ObjectPool
{
struct BlockNode //每一個結(jié)點類型
{
void* _memory; //指向一塊已經(jīng)分配的內(nèi)存
BlockNode * _next; //指向下一個結(jié)點
size_t _objNum; //記錄這塊內(nèi)存中對象的個數(shù)
BlockNode(size_t objNum)
:_objNum(objNum)
, _next(NULL)
{
_memory = malloc(_objNum*_itemSize);
}
~BlockNode()
{
free(_memory);
_memory = NULL;
_next = NULL;
_objNum = 0;
}
};
protected:
size_t _countIn; //當(dāng)前結(jié)點的在用的計數(shù)
BlockNode* _frist; //指向鏈表的頭
BlockNode* _last; //指向鏈表的尾
size_t _maxNum; //記錄內(nèi)存塊最大的容量
static size_t _itemSize; //單個對象的大小
T* _lastDelete; //指向最新釋放的那個對象的空間
public:
ObjectPool(size_t initNum = 32, size_t maxNum = 100000) //默認(rèn)最開始內(nèi)存塊有32個對象,一個內(nèi)存塊最大有maxNum個對象
:_countIn(0)
, _maxNum(maxNum)
, _lastDelete(NULL)
{
_frist = _last =new BlockNode(initNum); //先開辟一個結(jié)點,這個結(jié)點里面的內(nèi)存塊能夠存放initNum個對象
}
~ObjectPool()
{
Destory();
}
T* New() //分配內(nèi)存
{
if (_lastDelete) //先到釋放已經(jīng)用完并且換回來的內(nèi)存中去找
{
T* object = _lastDelete;
_lastDelete = *((T**)_lastDelete); //將_lastDelete轉(zhuǎn)換成T**,*引用再取出來T*,也就是取出前T*類型大小的單元
return new(object) T(); //把這塊內(nèi)存用從定位new初始化一下
}
//判斷還有沒有已經(jīng)分配的內(nèi)存且還未使用,如果沒有內(nèi)存的話就要再分配內(nèi)存
if (_countIn >= _last->_objNum) //大于等于表示沒有了,這時候就要分配內(nèi)存了
{
size_t size =2*_countIn;
if (size > _maxNum) //塊的最大大小不能超過maxNum,如果沒超過就以二倍增長
size = _maxNum;
_last->_next = new BlockNode(size);
_last = _last->_next;
_countIn = 0;
}
//還有已經(jīng)分配好的未被使用的內(nèi)存
T* object =(T*)((char*)_last->_memory + _countIn*_itemSize);
_countIn++;
return new(object) T(); //將這塊空間用重定位new初始化一下
}
void Destory()
{
BlockNode *cur = _frist;
while (cur)
{
BlockNode* del = cur;
cur = cur->_next;
delete del; //會自動調(diào)用~BlockNode()
}
_frist = _last = NULL;
}
void Delete(T* object) //釋放內(nèi)存
{
if (object)
{
object->~T();
*((T**)object) = _lastDelete; //將_lastDelete里面保存的地址存到tmp指向空間的前T*大小的空間里面
_lastDelete = object;
}
}
protected:
static size_t GetItemSize()
{
if (sizeof(T)>sizeof(T*))
{
return sizeof(T);
}
else
{
return sizeof(T*);
}
}
};
template<typename T>
size_t ObjectPool<T>::_itemSize =ObjectPool<T>::GetItemSize(); //類外初始化靜態(tài)變量_itemSize
總結(jié)
到此這篇關(guān)于C++設(shè)計一個簡單內(nèi)存池的文章就介紹到這了,更多相關(guān)C++設(shè)計內(nèi)存池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)LeetCode(35.搜索插入位置)
這篇文章主要介紹了C++實現(xiàn)LeetCode(35.搜索插入位置),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
C++項目開發(fā)實現(xiàn)圖書管理系統(tǒng)
這篇文章主要為大家詳細介紹了C++項目開發(fā)實現(xiàn)圖書管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
VisualStudio2022不支持.NET Framework 4.0項目解決辦法
本文主要介紹了VisualStudio2022不支持.NET Framework 4.0項目解決辦法,文中通過圖文的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09

