欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++內(nèi)存池的簡(jiǎn)單實(shí)現(xiàn)

 更新時(shí)間:2021年07月13日 11:41:24   作者:Moua  
內(nèi)存池是一種動(dòng)態(tài)內(nèi)存分配與管理技術(shù)。本文主要介紹了C++內(nèi)存池的簡(jiǎn)單實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、內(nèi)存池基礎(chǔ)知識(shí)

1、什么是內(nèi)存池

1.1 池化技術(shù)

池化技術(shù)是計(jì)算機(jī)中的一種設(shè)計(jì)模式,主要是指:將程序中經(jīng)常要使用的計(jì)算機(jī)資源預(yù)先申請(qǐng)出來(lái),由程序自己管理,程序在使用時(shí)直接從“池”中獲取,不僅保證了程序占有的資源數(shù)量同時(shí)減少資源的申請(qǐng)和釋放時(shí)間。常見(jiàn)的池化技術(shù)有內(nèi)存池、線程池、連接池等。

1.2 內(nèi)存池

內(nèi)存池是一種動(dòng)態(tài)內(nèi)存分配與管理技術(shù)。它的核心思想是:預(yù)先申請(qǐng)一段內(nèi)存空間,使用一種高效的數(shù)據(jù)結(jié)構(gòu)(哈希、鏈表)進(jìn)行管理,當(dāng)程序需要內(nèi)存時(shí)直接從內(nèi)存池中分配一塊內(nèi)存給程序,同樣當(dāng)使用完時(shí)在歸還給內(nèi)存池。這樣做的好處是,減少直接使用new/delete、malloc/free等API申請(qǐng)和釋放內(nèi)存的時(shí)間,提高程序運(yùn)行效率;同時(shí),程序每次直接使用new/delete、malloc/free從內(nèi)存中申請(qǐng)空間,會(huì)導(dǎo)致內(nèi)存碎片問(wèn)題,內(nèi)存池直接申請(qǐng)大塊內(nèi)存就減少了內(nèi)存碎片。

2、內(nèi)存池的作用

2.1 效率問(wèn)題

通常申請(qǐng)內(nèi)存都是通過(guò)new/delete、malloc/free接口直接從內(nèi)存的堆區(qū)申請(qǐng)一塊內(nèi)存,釋放也是直接釋放到堆中。頻繁的申請(qǐng)和釋放必然消耗大量時(shí)間,降低程序的運(yùn)行效率。

例如:假設(shè)每個(gè)鏈表的節(jié)點(diǎn)大小為16字節(jié),當(dāng)鏈表需要經(jīng)常插入節(jié)點(diǎn)時(shí),必然就需要頻繁的內(nèi)存申請(qǐng)操縱,每次從堆中申請(qǐng)16個(gè)字節(jié)都要一定的時(shí)間開(kāi)銷(xiāo),釋放內(nèi)存也需要時(shí)間開(kāi)銷(xiāo)。使用內(nèi)存池,我們可以直接從內(nèi)存中申請(qǐng)“一批節(jié)點(diǎn)”,當(dāng)程序需要內(nèi)存時(shí)不用直接去堆中申請(qǐng),直接將預(yù)先申請(qǐng)好的內(nèi)存分配給程序。

2.2 內(nèi)存碎片

頻繁的從內(nèi)存中申請(qǐng)小塊內(nèi)存會(huì)導(dǎo)致內(nèi)存碎片問(wèn)題。內(nèi)存碎片分為內(nèi)碎片和外碎片兩種。

1)外碎片

外碎片也就是我們常說(shuō)的內(nèi)存碎片。例如:我們每次從內(nèi)存中申請(qǐng)一塊16字節(jié)大小的內(nèi)存,內(nèi)存中就會(huì)存在很多16個(gè)字節(jié)大小的塊,當(dāng)該內(nèi)存釋放時(shí)就可能造成內(nèi)存碎片,如下圖:

內(nèi)存中空閑內(nèi)存大小為88字節(jié),但是我們能申請(qǐng)的最大內(nèi)存塊為21字節(jié)。

2)內(nèi)碎片

內(nèi)碎片是指已經(jīng)分配出去的內(nèi)存中存在的未使用的小塊內(nèi)存。內(nèi)存池技術(shù)雖然解決了內(nèi)存隨便但是又造成了內(nèi)碎片問(wèn)題,內(nèi)碎片不可避免但是可以通過(guò)程序的優(yōu)化減少內(nèi)存內(nèi)碎片。

例如:實(shí)際需要是申請(qǐng)10byte的內(nèi)存,定長(zhǎng)內(nèi)存池可能會(huì)進(jìn)行內(nèi)存對(duì)齊,一次性分配了16個(gè)字節(jié)的內(nèi)存,多余的6字節(jié)實(shí)際并未使用,這6字節(jié)就是內(nèi)存內(nèi)碎片。

3、內(nèi)存池技術(shù)的演進(jìn)

1)最最最最“簡(jiǎn)單”的內(nèi)存池

做一個(gè)鏈表,指向空閑的內(nèi)存。分配就是從鏈表中取出來(lái)一塊返回pop,釋放就是將內(nèi)存在push到鏈表中。需要做好歸并,標(biāo)記和保護(hù),防止內(nèi)存二次釋放問(wèn)題。

2)定長(zhǎng)內(nèi)存池

實(shí)現(xiàn)一個(gè)FreeList類(lèi),它的本質(zhì)是一個(gè)鏈表,節(jié)點(diǎn)是一塊固定大小的內(nèi)存,采用頭插和頭刪的方式申請(qǐng)釋放內(nèi)存。每個(gè)固定內(nèi)存分配器里面有兩個(gè)鏈表:OpenList用于存儲(chǔ)未分配的空閑內(nèi)存對(duì)象(FreeList對(duì)象),CloseList用于存儲(chǔ)已經(jīng)分配的內(nèi)存對(duì)象。

分配內(nèi)存就是從IOpenLsit中取出一個(gè)對(duì)象給程序,釋放內(nèi)存就是將對(duì)象push到CloseList里。當(dāng)內(nèi)存不夠時(shí),OpenList申請(qǐng)一個(gè)大塊內(nèi)存在切割成固定的長(zhǎng)度大小的小塊內(nèi)存。

3)C++STL庫(kù)中的內(nèi)存池

定長(zhǎng)內(nèi)存池存在的問(wèn)題就是只能申請(qǐng)固定長(zhǎng)度的內(nèi)存,而實(shí)際中我們需要申請(qǐng)的內(nèi)存大小可能是不管固定,在C++STL庫(kù)中,采用哈希表和定長(zhǎng)內(nèi)存池結(jié)合的方式實(shí)現(xiàn)了一個(gè)內(nèi)存池。具體如下

構(gòu)造多個(gè)定長(zhǎng)內(nèi)存池,以一個(gè)固定的對(duì)齊數(shù)進(jìn)行對(duì)齊(例如以8字節(jié)進(jìn)行對(duì)齊),第一個(gè)定長(zhǎng)內(nèi)存池的內(nèi)存對(duì)象大小為8(至少得能保證無(wú)論在64位還是32位系統(tǒng)下都可以保存下一個(gè)指針類(lèi)型),第二個(gè)內(nèi)存池對(duì)象大小為16...最后一個(gè)內(nèi)存池對(duì)象大小為128byte,當(dāng)申請(qǐng)的內(nèi)存大小超過(guò)128字節(jié)時(shí),通過(guò)二級(jí)空間配置器申請(qǐng)(直接從內(nèi)存中申請(qǐng))。

構(gòu)造一個(gè)哈希表,將不同大小的內(nèi)存對(duì)象掛在哈希表中。如下圖:

申請(qǐng)內(nèi)存:加入要申請(qǐng)的內(nèi)存大小為8字節(jié)直接在index  = 0處分配一塊內(nèi)存,當(dāng)然申請(qǐng)的內(nèi)存小于8字節(jié)時(shí)也會(huì)直接分配8字節(jié)的內(nèi)存。當(dāng)Free_list[index]為nullptr時(shí)從內(nèi)存中申請(qǐng)一塊內(nèi)存,切割成固定大小‘掛在'Free_list[index]位置。

釋放內(nèi)存:根據(jù)內(nèi)存對(duì)象大小,計(jì)算index在插入到哈希表中的index位置。

二、簡(jiǎn)易內(nèi)存池原理

1、整體設(shè)計(jì)

1.1 內(nèi)存池結(jié)構(gòu)

兩個(gè)鏈表,RequestMemory和ReleaseMemory。

RequestMemory鏈表存儲(chǔ)的是使用new或者malloc從物理內(nèi)存申請(qǐng)的還沒(méi)有被使用的內(nèi)存塊,是一個(gè)個(gè)的memNode節(jié)點(diǎn)。

ReleaseMemory鏈表存儲(chǔ)的是使用完釋放回來(lái)的固定大小的內(nèi)存塊。

1.2 申請(qǐng)內(nèi)存

  •  先在ReleaseMemory找,如果有內(nèi)存則直接pop使用
  • ReleaseMemory為nullptr時(shí),在RequestMemory中找。
  • RequestMemory的頭節(jié)點(diǎn)表示的是新申請(qǐng)的,申請(qǐng)內(nèi)存時(shí)只需要在頭結(jié)點(diǎn)中找,判斷頭結(jié)點(diǎn)的useCount和sumCount是否相等。當(dāng)useCount等于sumCount時(shí)表示已經(jīng)用完了,就需要去物理內(nèi)存中申請(qǐng),否則直接從表頭push一塊。
  • 去物理內(nèi)存申請(qǐng)內(nèi)存時(shí),申請(qǐng)的大小是上一次申請(qǐng)內(nèi)存塊大小的二倍,并將申請(qǐng)的內(nèi)存塊push到RequestMemory頭部。

1.3 釋放內(nèi)存

釋放內(nèi)存時(shí),直接將要釋放的內(nèi)存push到ReleaseMemory的頭部即可。

2、詳細(xì)剖析

2.1 blockNode結(jié)構(gòu)

blockNode表示一個(gè)個(gè)新申請(qǐng)的內(nèi)存塊,用一個(gè)結(jié)構(gòu)體進(jìn)行管理。blockNode成員如下:

  • void* _memory:表示新申請(qǐng)的內(nèi)存塊的首地址
  • BlockNode * _next:存儲(chǔ)next節(jié)點(diǎn)
  • _objNum:內(nèi)存塊對(duì)象的個(gè)數(shù)

注意:blockNode的大小每次都是上一次的二倍,是一個(gè)質(zhì)數(shù)增長(zhǎng),因此應(yīng)該設(shè)置一個(gè)上限,當(dāng)?shù)竭_(dá)一定大小后進(jìn)行線性增長(zhǎng)。這里規(guī)定,最大內(nèi)存塊的大小為100000*sizeof(T),T表示的是申請(qǐng)的節(jié)點(diǎn)類(lèi)型。

2.2 單個(gè)對(duì)象的大小

這里的單個(gè)對(duì)象指的ReleaseMemory的節(jié)點(diǎn)大小,當(dāng)用戶申請(qǐng)的內(nèi)存大小sizeof(T)小于sizeof(T*)時(shí),為了能夠?qū)⒃搶?duì)象鏈接到ReleaseMemory中,應(yīng)該按照T*進(jìn)行分配。

3、性能比較

分別使用malloc/free、new/delete、memPool申請(qǐng)和釋放110000個(gè)內(nèi)存,時(shí)間如下:

三、簡(jiǎn)易內(nèi)存池完整源碼

#include<iostream>
#include<vector>
#include<ctime>
using namespace std;
 
template<class T>
class MemPool
{
private:
	//內(nèi)存塊結(jié)構(gòu)
	typedef struct BlockNode
	{
		void* _memory;//內(nèi)存塊地址
		BlockNode* _next;//下一個(gè)blockNode
		size_t _objNum;//內(nèi)存塊對(duì)象的個(gè)數(shù)
		//構(gòu)造函數(shù)---num表示申請(qǐng)對(duì)象的個(gè)數(shù)
		BlockNode(size_t num)
			:_objNum(num),
			_next(nullptr)
		{
			_memory = malloc(_objNum*_size);
		}
 
		~BlockNode()
		{
			free(_memory);
			_memory = nullptr;
			_next = nullptr;
			_objNum = 0;
		}
	}BlockNode;
protected:
	static size_t _size;//單個(gè)對(duì)象的大小
	T* _releaseMemory = nullptr;//釋放的內(nèi)存
	BlockNode* _requestMemory;//申請(qǐng)的內(nèi)存塊
	size_t _maxNum;//內(nèi)存塊最大的大小
	size_t _useCount;//當(dāng)前內(nèi)存塊已經(jīng)使用的對(duì)象個(gè)數(shù)
protected:
	//設(shè)置單個(gè)對(duì)象的大小
	static size_t setSize()
	{
		return (sizeof(T) >= sizeof(T*) ? sizeof(T):sizeof(T*));
	}
public:
	MemPool()
		:_useCount(0),
		_releaseMemory(nullptr),
		_maxNum(100000*_size)
	{
		//開(kāi)始先申請(qǐng)32個(gè)_size大小的空間
		_requestMemory = new BlockNode(32);
	}
 
	~MemPool()
	{
		BlockNode *cur = _requestMemory;
		while (cur)
		{
			BlockNode* del = cur;
			cur = cur->_next;
			delete del;            //會(huì)自動(dòng)調(diào)用~BlockNode()
		}
	}
 
	T* New()
	{
		//先在releaseMemory中找
		if (_releaseMemory)
		{
			T* obj = _releaseMemory;
			_releaseMemory = *((T**)_releaseMemory);//releaseMemory的前幾個(gè)字節(jié)存儲(chǔ)的是下一個(gè)節(jié)點(diǎn)的地址
			return obj;
		}
		else
		{
			//判斷requesetMemory中是否還有空閑內(nèi)存
			if (_requestMemory->_objNum == _useCount)
			{
				//取物理內(nèi)存中申請(qǐng)一塊內(nèi)存
				size_t size = 2 * _useCount >= _maxNum ? _maxNum : 2 * _useCount;
				BlockNode* newBlock = new BlockNode(size);
 
				newBlock->_next = _requestMemory;
				_requestMemory = newBlock;
				_useCount = 0;
			}
			//走到這里,一定有內(nèi)存
			T* obj = (T*)((char*)_requestMemory->_memory+_useCount*_size);
 
			_useCount++;
			return new(obj)T();//用定位new對(duì)這塊空間初始化
		}
	}
 
	void Delete(T* obj)
	{
		if (obj)
		{
			obj->~T();
 
			*((T**)obj) = _releaseMemory;
			_releaseMemory = obj;
		}
	}
};
 
//靜態(tài)成員變量,類(lèi)外初始化
template<typename T>
size_t MemPool<T>::_size = MemPool<T>::setSize();
 
struct TreeNode
{
	int _val;
	TreeNode* _left;
	TreeNode* _right;
};
void test1()
{
	MemPool<TreeNode> mp;
 
	vector<TreeNode*> v;
	for (int i = 0; i < 10; i++)
	{
		TreeNode* mem = mp.New();
		v.push_back(mem);
	}
 
	for (int i = 0; i < 10; i++)
	{
		mp.Delete(v[i]);
	}
}

到此這篇關(guān)于C++內(nèi)存池的簡(jiǎn)單實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 內(nèi)存池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 利用Matlab編寫(xiě)簡(jiǎn)易版連連看小游戲

    利用Matlab編寫(xiě)簡(jiǎn)易版連連看小游戲

    連連看作為經(jīng)典的小游戲,一定是很多人的回憶吧。本文將用Matlab實(shí)現(xiàn)這一經(jīng)典的游戲,文中示例代碼具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 詳解C語(yǔ)言中Char型指針數(shù)組與字符數(shù)組的區(qū)別

    詳解C語(yǔ)言中Char型指針數(shù)組與字符數(shù)組的區(qū)別

    這篇文章主要介紹了詳解C語(yǔ)言中Char型指針數(shù)組與字符數(shù)組的區(qū)別的相關(guān)資料,希望通過(guò)本文能幫助到大家掌握理解這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能

    Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能

    這篇文章主要為大家詳細(xì)介紹了Qt TCP實(shí)現(xiàn)簡(jiǎn)單通信功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 一文詳解QDialog中exec與open的區(qū)別

    一文詳解QDialog中exec與open的區(qū)別

    這篇文章主要為大家詳細(xì)介紹了QDialog中exec與open的區(qū)別,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Qt有一定的幫助,需要的可以參考一下
    2023-03-03
  • C++11中std::packaged_task的使用詳解

    C++11中std::packaged_task的使用詳解

    這篇文章主要介紹了C++11中std::packaged_task的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • C/C++下讀取ENVI柵格文件格式的示例代碼

    C/C++下讀取ENVI柵格文件格式的示例代碼

    ENVI使用的是通用柵格數(shù)據(jù)格式,包含一個(gè)簡(jiǎn)單的二進(jìn)制文件( a simple flat binary )和一個(gè)相關(guān)的ASCII(文本)的頭文件,下面我們就來(lái)看看如何使用C++讀取ENVI柵格文件格式吧
    2024-10-10
  • C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法

    C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法

    這篇文章主要介紹了C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • C++實(shí)現(xiàn)的歸并排序算法詳解

    C++實(shí)現(xiàn)的歸并排序算法詳解

    這篇文章主要介紹了C++實(shí)現(xiàn)的歸并排序算法,結(jié)合實(shí)例形式詳細(xì)分析了歸并排序算法的原理、實(shí)現(xiàn)步驟、操作技巧與使用方法,需要的朋友可以參考下
    2017-05-05
  • 基于Qt編寫(xiě)簡(jiǎn)易的視頻播放器

    基于Qt編寫(xiě)簡(jiǎn)易的視頻播放器

    這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)編寫(xiě)簡(jiǎn)易的視頻播放器,可以支持pbonon/qmediaplayer/ffmpeg/vlc/mpv等多種內(nèi)核,感興趣的可以學(xué)習(xí)一下
    2022-12-12
  • windows消息和消息隊(duì)列實(shí)例詳解

    windows消息和消息隊(duì)列實(shí)例詳解

    這篇文章主要介紹了windows消息和消息隊(duì)列實(shí)例詳解,詳細(xì)講述了Windows的消息機(jī)制與原理,對(duì)于深入理解和學(xué)習(xí)Windows應(yīng)用程序設(shè)計(jì)有不錯(cuò)的借鑒價(jià)值,需要的朋友可以參考下
    2014-10-10

最新評(píng)論