• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 311, comments - 0, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            Boost庫是一個可移植的開源C++函數(shù)庫,鑒于STL(標(biāo)準(zhǔn)模板庫)已經(jīng)成為C++語言的一個組成部分,可以毫不夸張的說,Boost是目前影響最大的通用C++庫。Boost庫由C++標(biāo)準(zhǔn)委員會庫工作組成員發(fā)起,其中有些內(nèi)容有望成為下一代C++標(biāo)準(zhǔn)庫內(nèi)容,是一個“準(zhǔn)”標(biāo)準(zhǔn)庫。

              Boost內(nèi)存池,即boost.pool庫,是由Boost提供的一個用于內(nèi)存池管理的開源C++庫。作為Boost中影響較大的一個庫,Pool已經(jīng)被廣泛使用。

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

              “池”是在計算機技術(shù)中經(jīng)常使用的一種設(shè)計模式,其內(nèi)涵在于:將程序中需要經(jīng)常使用的核心資源先申請出來,放到一個池內(nèi),由程序自己管理,這樣可以提高資源的使用效率,也可以保證本程序占有的資源數(shù)量。經(jīng)常使用的池技術(shù)包括內(nèi)存池、線程池和連接池等,其中尤以內(nèi)存池和線程池使用最多。

              內(nèi)存池(Memory Pool)是一種動態(tài)內(nèi)存分配與管理技術(shù)。通常情況下,程序員習(xí)慣直接使用new、delete、malloc、free等API申請分配和釋放內(nèi)存,導(dǎo)致的后果時:當(dāng)程序長時間運行時,由于所申請內(nèi)存塊的大小不定,頻繁使用時會造成大量的內(nèi)存碎片從而降低程序和操作系統(tǒng)的性能。內(nèi)存池則是在真正使用內(nèi)存之前,先申請分配一大塊內(nèi)存(內(nèi)存池)留作備用,當(dāng)程序員申請內(nèi)存時,從池中取出一塊動態(tài)分配,當(dāng)程序員釋放內(nèi)存時,將釋放的內(nèi)存再放入池內(nèi),并盡量與周邊的空閑內(nèi)存塊合并。若內(nèi)存池不夠時,則自動擴大內(nèi)存池,從操作系統(tǒng)中申請更大的內(nèi)存池。

              內(nèi)存池的應(yīng)用場景

              早期的內(nèi)存池技術(shù)是為了專門解決那種頻繁申請和釋放相同大小內(nèi)存塊的程序,因此早期的一些內(nèi)存池都是用相同大小的內(nèi)存塊鏈表組織起來的。

              Boost的內(nèi)存池則對內(nèi)存塊的大小是否相同沒有限制,因此只要是頻繁動態(tài)申請釋放內(nèi)存的長時間運行程序,都適用Boost內(nèi)存池。這樣可以有效減少內(nèi)存碎片并提高程序運行效率。

              安裝

              Boost的pool庫是以C++頭文件的形式提供的,不需要安裝,也沒有l(wèi)ib或者dll文件,僅僅需要將頭文件包含到你的C++工程中就可以了。Boost的最新版本可以到http://www.boost.org/下載。

              2. 內(nèi)存池的特征

              2.1 無內(nèi)存泄露

              正確的使用內(nèi)存池的申請和釋放函數(shù)不會造成內(nèi)存泄露,更重要的是,即使不正確的使用了申請和釋放函數(shù),內(nèi)存池中的內(nèi)存也會在進程結(jié)束時被全部自動釋放,不會造成系統(tǒng)的內(nèi)存泄露。

              2.2 申請的內(nèi)存數(shù)組沒有被填充

              例如一個元素的內(nèi)存大小為A,那么元素數(shù)組若包含n個元素,則該數(shù)組的內(nèi)存大小必然是A*n,不會有多余的內(nèi)存來填充該數(shù)組。盡管每個元素也許包含一些填充的東西。

              2.3 任何數(shù)組內(nèi)存塊的位置都和使用operator new[]分配的內(nèi)存塊位置一致

              這表明你仍可以使用那些通過數(shù)組指針計算內(nèi)存塊位置的算法。

              2.4 內(nèi)存池要比直接使用系統(tǒng)的動態(tài)內(nèi)存分配快

              這個快是概率意義上的,不是每個時刻,每種內(nèi)存池都比直接使用new或者malloc快。例如,當(dāng)程序使用內(nèi)存池時內(nèi)存池恰好處于已經(jīng)滿了的狀態(tài),那么這次內(nèi)存申請會導(dǎo)致內(nèi)存池自我擴充,肯定比直接new一塊內(nèi)存要慢。但在大部分時候,內(nèi)存池要比new或者malloc快很多。


              3. 內(nèi)存池效率測試

              3.1 測試1:連續(xù)申請和連續(xù)釋放

              分別用內(nèi)存池和new連續(xù)申請和連續(xù)釋放大量的內(nèi)存塊,比較其運行速度,代碼如下:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            const int MAXLENGTH = 100000;

            int main ( )
            {
            boost::pool
            <> p(sizeof(int));
            int* vec1[MAXLENGTH];
            int* vec2[MAXLENGTH];

            clock_t clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            vec1[i]
            = static_cast<int*>(p.malloc());
            }

            for (int i = 0; i < MAXLENGTH; ++i)
            {
            p.free(vec1[i]);
            vec1[i]
            = NULL;
            }


            clock_t clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            vec2[i]
            = new int;
            }

            for (int i = 0; i < MAXLENGTH; ++i)
            {
            delete vec2[i];
            vec2[i]
            = NULL;
            }


            clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            return 0;
            }

              測試環(huán)境:VS2008,WindowXP SP2,Pentium 4 CPU雙核,1.5GB內(nèi)存。

              結(jié)論:在連續(xù)申請和連續(xù)釋放10萬塊內(nèi)存的情況下,使用內(nèi)存池耗時是使用new耗時的47.46%。


              3.2 測試2:反復(fù)申請和釋放小塊內(nèi)存

              代碼如下:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            const int MAXLENGTH = 500000;

            int main ( )
            {
            boost::pool
            <> p(sizeof(int));

            clock_t clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            int * t = static_cast<int*>(p.malloc());
            p.free(t);
            }

            clock_t clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            int* t = new int;
            delete t;
            }

            clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            return 0;
            }

              測試結(jié)果如下:

              結(jié)論:在反復(fù)申請和釋放50萬次內(nèi)存的情況下,使用內(nèi)存池耗時是使用new耗時的64.34%。


              3.3 測試3:反復(fù)申請和釋放C++對象

              C++對象在動態(tài)申請和釋放時,不僅要進行內(nèi)存操作,同時還要調(diào)用構(gòu)造和析購函數(shù)。因此有必要對C++對象也進行內(nèi)存池的測試。

              代碼如下:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            const int MAXLENGTH = 500000;
            class A
            {
            public:
            A()
            {
            m_i
            ++;
            }

            ~A( )
            {
            m_i
            --;
            }

            private:
            int m_i;
            }
            ;

            int main ( )
            {
            object_pool
            <A> q;

            clock_t clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            A
            * a = q.construct();
            q.destroy(a);
            }


            clock_t clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            clock_begin
            = clock();
            for (int i = 0; i < MAXLENGTH; ++i)
            {
            A
            * a = new A;
            delete a;
            }

            clock_end
            = clock();
            cout
            <<"程序運行了 "<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            return 0;
            }

              測試結(jié)果如下:

              結(jié)論:在反復(fù)申請和釋放50萬個C++對象的情況下,使用內(nèi)存池耗時是使用new耗時的112.03%。這是因為內(nèi)存池的construct和destroy函數(shù)增加了函數(shù)調(diào)用次數(shù)的原因。這種情況下使用內(nèi)存池并不能獲得性能上的優(yōu)化。


              4. Boost內(nèi)存池的分類

              Boost內(nèi)存池按照不同的理念分為四類。主要是兩種理念的不同造成了這樣的分類。

              一是Object Usage和Singleton Usage的不同。Object Usage意味著每個內(nèi)存池都是一個可以創(chuàng)建和銷毀的對象,一旦內(nèi)存池被銷毀則其所分配的所有內(nèi)存都會被釋放。Singleton Usage意味著每個內(nèi)存池都是一個被靜態(tài)分配的對象,直至程序結(jié)束才會被銷毀,這也意味著這樣的內(nèi)存池是多線程安全的。只有使用release_memory或者 purge_memory方法才能釋放內(nèi)存。

              二是內(nèi)存溢出的處理方式。第一種方式是返回NULL代表內(nèi)存池溢出了;第二種方式是拋出異常代表內(nèi)存池溢出。

              根據(jù)以上的理念,boost的內(nèi)存池分為四種。

              4.1 Pool

              Pool是一個Object Usage的內(nèi)存池,溢出時返回NULL。

              4.2 object_pool

              object_pool與pool類似,唯一的區(qū)別是當(dāng)其分配的內(nèi)存釋放時,它會嘗試調(diào)用該對象的析購函數(shù)。

              4.3 singleton_pool

              singleton_pool是一個Singleton Usage的內(nèi)存池,溢出時返回NULL。

              4.4 pool_alloc

              pool_alloc是一個Singleton Usage的內(nèi)存池,溢出時拋出異常。

              5. 內(nèi)存池溢出的原理與解決方法

              5.1 必然溢出的內(nèi)存

              內(nèi)存池簡化了很多內(nèi)存方面的操作,也避免了一些錯誤使用內(nèi)存對程序造成的損害。但是,使用內(nèi)存池時最需要注意的一點是要處理內(nèi)存池溢出的情況。

              沒有不溢出的內(nèi)存,看看下面的代碼:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            int _tmain(int argc, _TCHAR* argv[])
            {
            clock_t clock_begin
            = clock();
            int iLength = 0;
            for (int i = 0; ;++i)
            {
            void* p = malloc(1024*1024);
            if (p == NULL)
            {
            break;
            }

            ++iLength;
            }

            clock_t clock_end
            = clock();
            cout
            <<"共申請了"<<iLength<<"M內(nèi)存,程序運行了"<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;
            return 0;
            }

              運行的結(jié)果是“共申請了1916M內(nèi)存,程序運行了 69421 個系統(tǒng)時鐘”,意思是在分配了1916M內(nèi)存后,malloc已經(jīng)不能夠申請到1M大小的內(nèi)存塊了。


              內(nèi)存池在底層也是調(diào)用了malloc函數(shù),因此內(nèi)存池也是必然會溢出的。而且內(nèi)存池可能會比直接調(diào)用malloc更早的溢出,看看下面的代碼:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            int _tmain(int argc, _TCHAR* argv[])
            {
            boost::pool
            <> pl(1024*1024);
            clock_t clock_begin
            = clock();
            int iLength = 0;
            for (int i = 0; ;++i)
            {
            void* p = pl.malloc();
            if (p == NULL)
            {
            break;
            }

            ++iLength;
            }

            clock_t clock_end
            = clock();
            cout
            <<"共申請了"<<iLength<<"M內(nèi)存,程序運行了"<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;
            return 0;
            }

              運行的結(jié)果是“共申請了992M內(nèi)存,程序運行了 1265 個系統(tǒng)時鐘”,意思是在分配了992M內(nèi)存后,內(nèi)存池已經(jīng)不能夠申請到1M大小的內(nèi)存塊了。

              5.2 內(nèi)存池的基本原理

              從上面的兩個測試可以看出內(nèi)存池要比malloc溢出早,我的機器內(nèi)存是1.5G,malloc分配了1916M才溢出(顯然分配了虛擬內(nèi)存),而內(nèi)存池只分配了992M就溢出了。第二點是內(nèi)存池溢出快,只用了1265微秒就溢出了,而malloc用了69421微秒才溢出。

              這些差別是內(nèi)存池的處理機制造成的,內(nèi)存池對于內(nèi)存分配的算法如下,以pool內(nèi)存池為例:

              1. pool初始化時帶有一個塊大小的參數(shù)memSize,那么pool剛開始會申請一大塊內(nèi)存,例如其大小為32*memSize。當(dāng)然它還會申請一些空間用以管理鏈表,為方便述說,這里忽略這些內(nèi)存。

              2. 用戶不停的申請大小為memSize的內(nèi)存,終于超過了內(nèi)存池的大小,于是內(nèi)存池啟動重分配機制;

              3. 重分配機制會再申請一塊大小為原內(nèi)存池大小兩倍的內(nèi)存(那么第一次會申請64*memSize),然后將這塊內(nèi)存加到內(nèi)存池的管理鏈表末尾;

              4. 用戶繼續(xù)申請內(nèi)存,終于又一次超過了內(nèi)存池的大小,于是又一次啟動重分配機制,直至重分配時無法申請到新的內(nèi)存塊。

              5. 由于每次都是兩倍于原內(nèi)存,因此當(dāng)內(nèi)存池大小達到了992M時,再一次申請就需要1984M,但是malloc最多只能申請到1916M,因此malloc失敗,內(nèi)存池溢出。

              通過以上原理也可以理解為什么內(nèi)存池溢出比malloc溢出要快得多,因為它是以2的指數(shù)級來擴大內(nèi)存池,真正調(diào)用malloc的次數(shù)約等于log2(1916),而malloc是實實在在進行了1916次調(diào)用。所以內(nèi)存池只用了1秒多就溢出了,而malloc用了69秒。

              5.3 內(nèi)存池溢出的解決方法

              對于malloc造成的內(nèi)存溢出,一般來說沒有太多辦法可想。基本上就是報一個異常或者錯誤,然后讓用戶關(guān)閉程序。當(dāng)然有的程序會有內(nèi)存自我管理功能,可以讓用戶選擇關(guān)閉一切次要功能來維持主要功能的繼續(xù)運行。

              而對于內(nèi)存池的溢出,還是可以想一些辦法的,因為畢竟系統(tǒng)內(nèi)存還有潛力可挖。

              第一個方法是盡量延緩內(nèi)存池的溢出,做法是在程序啟動時就盡量申請最大的內(nèi)存池,如果在程序運行很久后再申請,可能OS因為內(nèi)存碎片增多而不能提供最大的內(nèi)存池。其方法是在程序啟動時就不停的申請內(nèi)存直到內(nèi)存池溢出,然后清空內(nèi)存池并開始正常工作。由于內(nèi)存池并不會自動減小,所以這樣可以一直維持內(nèi)存池保持最大狀態(tài)。

              第二個方法是在內(nèi)存池溢出時使用第二個內(nèi)存池,由于第二個內(nèi)存池可以繼續(xù)申請較小塊的內(nèi)存,所以程序可繼續(xù)運行。代碼如下:

            #div_code img{border:0px;}
            #include "stdafx.h"
            #include
            <iostream>
            #include
            <ctime>
            #include
            <vector>
            #include
            <boost/pool/pool.hpp>
            #include
            <boost/pool/object_pool.hpp>
            using namespace std;
            using namespace boost;

            int _tmain(int argc, _TCHAR* argv[])
            {
            boost::pool
            <> pl(1024*1024);
            clock_t clock_begin
            = clock();
            int iLength = 0;
            for (int i = 0; ;++i)
            {
            void* p = pl.malloc();
            if (p == NULL)
            {
            break;
            }

            ++iLength;
            }

            clock_t clock_end
            = clock();
            cout
            <<"共申請了"<<iLength<<"M內(nèi)存,程序運行了"<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            clock_begin
            = clock();
            iLength
            = 0;
            boost::pool
            <> pl2(1024*1024);
            for (int i = 0; ;++i)
            {
            void* p = pl2.malloc();
            if (p == NULL)
            {
            break;
            }

            ++iLength;
            }

            clock_end
            = clock();
            cout
            <<"又申請了"<<iLength<<"M內(nèi)存,程序運行了"<<clock_end-clock_begin<<" 個系統(tǒng)時鐘"<<endl;

            return 0;
            }

              運行結(jié)果如下:

              結(jié)果表明在第一個內(nèi)存池溢出后,第二個內(nèi)存池又提供了480M的內(nèi)存。

              5.4 內(nèi)存池溢出的終極方案

              如果無論如何都不能再申請到新的內(nèi)存了,那么還是老老實實告訴用戶重啟程序吧。

            亚洲精品无码久久久久久| 亚洲国产精品久久久久| 欧美一区二区久久精品| 久久久国产精华液| 草草久久久无码国产专区| 久久久久亚洲av成人无码电影 | 成人综合伊人五月婷久久| 久久久中文字幕| 一本久久a久久精品亚洲| 99久久精品国产一区二区| 四虎国产精品成人免费久久| 国产三级久久久精品麻豆三级 | 激情伊人五月天久久综合| 久久国产福利免费| 久久AV高清无码| 亚洲中文字幕无码久久2017| 99久久99久久精品国产片果冻| 国产69精品久久久久9999APGF| 国产精品美女久久久久AV福利| 亚洲va久久久噜噜噜久久狠狠| 欧美激情精品久久久久久| 久久香蕉国产线看观看乱码 | yellow中文字幕久久网| 97久久天天综合色天天综合色hd | 久久99久久99精品免视看动漫| 无码任你躁久久久久久老妇| 91久久精品国产成人久久| 99久久婷婷免费国产综合精品| 无码国内精品久久人妻| 精品久久人人爽天天玩人人妻 | 久久久久av无码免费网| 91麻豆国产精品91久久久| 日韩一区二区三区视频久久| 久久久精品久久久久久| 久久国产三级无码一区二区| 大蕉久久伊人中文字幕| 狠狠精品久久久无码中文字幕| 国产精品九九久久免费视频| 亚洲午夜无码久久久久| 97久久国产亚洲精品超碰热| 999久久久国产精品|