您好,登錄后才能下訂單哦!
這篇文章主要介紹C++設計一個簡單內存池的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
什么是內存池???
通常我們用new或malloc來分配內存的話,由于申請的大小不確定,所以當頻繁的使用時會造成內存碎片和效率的降低。為了克服這種問題我們提出了內存池的概念。內存池是一種內存分配方式。內存池的優點就是可以有效的減少內存碎片化,分配內存更快速,減少內存泄漏等優點。
內存池是在真正使用內存之前,先申請分配一個大的內存塊留作備用。當真正需要使用內存的時候,就從內存池中分配一塊內存使用,當使這塊用完了之后再還給內存池。若是內存塊不夠了就向內存再申請一塊大的內存塊。
可以看出這樣做有兩個好處:
1、由于向內存申請的內存塊都是比較大的,所以能夠降低外碎片問題。
2、一次性向內存申請一塊大的內存慢慢使用,避免了頻繁的向內存請求內存操作,提高內存分配的效率。
內存碎片化:
造成堆利用率很低的一個主要原因就是內存碎片化。如果有未使用的存儲器,但是這塊存儲器不能用來滿足分配的請求,這時候就會產生內存碎片化問題。內存碎片化分為內部碎片和外部碎片。
內碎片:
內部碎片是指一個已分配的塊比有效載荷大時發生的。(舉個栗子:假設以前分配了10個大小的字節,現在只用了5個字節,則剩下的5個字節就會內碎片)。內部碎片的大小就是已經分配的塊的大小和他們的有效載荷之差的和。因此內部碎片取決于以前請求內存的模式和分配器實現的模式。
外碎片: 外部碎片就是當空閑的存儲器的和計起來足夠滿足一個分配請求,但是沒有一個單獨的空閑塊足夠大可以處理這個請求。外部碎片取決于以前的請求內存的模式和分配器的實現模式,還取決于于將來的內存請求模式。所以外部碎片難以量化。
下面介紹一種簡單的內存池,它是針對于某種對象實現的。 我們可以用一個鏈表實現這個內存池,鏈表上的每個結點都是一個對象池,如果我們需要申請空間的話,直接去內存池里面申請空間,當用完之后再還給內存池。
內存池的設計主要包含三步:
1、初始化
在創建內存池的時候為內存池分配了一塊很大的內存,便于以后的使用。
2、分配內存
當需要內存的時候就去內存池里面分配內存。
3、回收內存
當從內存池里面分配來的內存使用完畢之后,需要將這塊內存還給內存池。
設計上面這個內存池最重要的問題就是如何重復利用釋放回來的內存,讓利用率達到最高???
但是如果當對象的大小小于對象指針的時候,也就是一個對象的空間存不下一個指針的大小,這時候就不可避免的產生內碎片。 例如:為T類型對象開辟對象池,sizeof(T)<sizeof(T*),這時候我們就要為一個T類型對象申請sizeof(T*)大小的內存。
代碼實現: #pragma once #include<iostream> using namespace std; //用鏈表來實現內存池,每一個結點都掛有一塊內存 template<typename T> class ObjectPool { struct BlockNode //每一個結點類型 { void* _memory; //指向一塊已經分配的內存 BlockNode * _next; //指向下一個結點 size_t _objNum; //記錄這塊內存中對象的個數 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; //當前結點的在用的計數 BlockNode* _frist; //指向鏈表的頭 BlockNode* _last; //指向鏈表的尾 size_t _maxNum; //記錄內存塊最大的容量 static size_t _itemSize; //單個對象的大小 T* _lastDelete; //指向最新釋放的那個對象的空間 public: ObjectPool(size_t initNum = 32, size_t maxNum = 100000) //默認最開始內存塊有32個對象,一個內存塊最大有maxNum個對象 :_countIn(0) , _maxNum(maxNum) , _lastDelete(NULL) { _frist = _last =new BlockNode(initNum); //先開辟一個結點,這個結點里面的內存塊能夠存放initNum個對象 } ~ObjectPool() { Destory(); } T* New() //分配內存 { if (_lastDelete) //先到釋放已經用完并且換回來的內存中去找 { T* object = _lastDelete; _lastDelete = *((T**)_lastDelete); //將_lastDelete轉換成T**,*引用再取出來T*,也就是取出前T*類型大小的單元 return new(object) T(); //把這塊內存用從定位new初始化一下 } //判斷還有沒有已經分配的內存且還未使用,如果沒有內存的話就要再分配內存 if (_countIn >= _last->_objNum) //大于等于表示沒有了,這時候就要分配內存了 { size_t size =2*_countIn; if (size > _maxNum) //塊的最大大小不能超過maxNum,如果沒超過就以二倍增長 size = _maxNum; _last->_next = new BlockNode(size); _last = _last->_next; _countIn = 0; } //還有已經分配好的未被使用的內存 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; //會自動調用~BlockNode() } _frist = _last = NULL; } void Delete(T* object) //釋放內存 { 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(); //類外初始化靜態變量_itemSize
以上是“C++設計一個簡單內存池的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。