• <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>
            xiaoguozi's Blog
            Pay it forword - 我并不覺的自豪,我所嘗試的事情都失敗了······習(xí)慣原本生活的人不容易改變,就算現(xiàn)狀很糟,他們也很難改變,在過程中,他們還是放棄了······他們一放棄,大家就都是輸家······讓愛傳出去,很困難,也無法預(yù)料,人們需要更細心的觀察別人,要隨時注意才能保護別人,因為他們未必知道自己要什么·····
            經(jīng)典語錄1:

            哈佛有一個著名的理論:人的差別在于業(yè)余時間,而一個人的命運決定于晚上8點到10點之間。每晚抽出2個小時的時間用來閱讀、進修、思考或參加有意的演講、討論,你會發(fā)現(xiàn),你的人生正在發(fā)生改變,堅持數(shù)年之后,成功會向你招手。不要每天抱著QQ/MSN/游戲/電影/肥皂劇……奮斗到12點都舍不得休息,看就看一些勵志的影視或者文章,不要當(dāng)作消遣;學(xué)會思考人生,學(xué)會感悟人生,慢慢的,你的人生將會發(fā)生變化……

            經(jīng)典語錄2: 

            無論你的收入是多少,記得分成五份進行規(guī)劃投資:增加對身體的投資,讓身體始終好用;增加對社交的投資,擴大你的人脈;增加對學(xué)習(xí)的投資,加強你的自信;增加對旅游的投資,擴大你的見聞;增加對未來的投資,增加你的收益。好好規(guī)劃落實,你會發(fā)現(xiàn)你的人生逐步會有大量盈余。

            經(jīng)典語錄3: 

            過去的一頁,能不翻就不要翻,翻落了灰塵會迷了雙眼。有些人說不出哪里好,但就是誰都替代不了! 那些以前說著永不分離的人,早已經(jīng)散落在天涯了。收拾起心情,繼續(xù)走吧,錯過花,你將收獲雨,錯過這一個,你才會遇到下一個。

            經(jīng)典語錄4: 

            被人誤解的時候能微微的一笑,這是一種素養(yǎng);受委屈的時候能坦然的一笑,這是一種大度;吃虧的時候能開心的一笑,這是一種豁達;無奈的時候能達觀的一笑,這是一種境界;危難的時候能泰然一笑,這是一種大氣;被輕蔑的時候能平靜的一笑,這是一種自信;失戀的時候能輕輕的一笑,這是一種灑脫。

            經(jīng)典語錄5: 

            人生途中,有些是無法逃避的,比如命運;有些是無法更改的,比如情緣;有些是難以磨滅的,比如記憶;有些是難以擱置的,比如愛戀……與其被動地承受,不如勇敢地面對;與其鳥宿檐下,不如擊翅風(fēng)雨;與其在沉默中孤寂,不如在抗?fàn)幹斜l(fā)……路越艱,阻越大,險越多,只要走過去了,人生就會更精彩。

            經(jīng)典語錄6: 

            你改變不了環(huán)境,但你可以改變自己;你改變不了事實,但你可以改變態(tài)度;你改變不了過去,但你可以改變現(xiàn)在;你不能控制他人,但你可以掌握自己;你不能預(yù)知明天,但你可以把握今天;你不可以樣樣順利,但你可以事事盡心;你不能延伸生命的長度,但你可以決定生命的寬度。

            經(jīng)典語錄7: 

            魅力女人:1、善于發(fā)現(xiàn)生活里的美。2、養(yǎng)成看書的習(xí)慣。3、擁有品位。4、跟有思想的人交朋友。5、遠離泡沫偶像劇。6、學(xué)會忍耐與寬容。7、培養(yǎng)健康的心態(tài),重視自己的身體。8、離開任何一個男人,都會活得很好。9、有著理財?shù)膭訖C,學(xué)習(xí)投資經(jīng)營。10、尊重感情,珍惜緣分。

            經(jīng)典語錄8: 

            愚人向遠方尋找快樂,智者則在自己身旁培養(yǎng)快樂。生活里的每一個細節(jié)都蘊藏著快樂,只是在于你是否感受到了而已??鞓分娜?,每一件事,每一個人身上,他都能發(fā)現(xiàn)能令自己歡悅的因素來,并讓快樂擴張,鼓舞和影響了周圍的人。

            經(jīng)典語錄9: 

            【給自己安慰的10句溫馨話】1、最重要的是今天的心;2、別總是自己跟自己過不去;3、用心做自己該做的事;4、不要過于計較別人評價;5、每個人都有自己的活法;6、喜歡自己才會擁抱生活;7、不必一味討好別人;8、木已成舟便要順其自然;9、不妨?xí)簳r丟開煩心事;10、自己感覺幸福就是幸福。

            經(jīng)典語錄10: 

            沒有永遠的緣份,沒有永遠的生命,我們所能擁有的,可能只是平凡的一生。然而因為有你,生命便全然不同,不用誓言,不必承諾,我們只需依了愛緣,以目光為媒,印證三生石上的約定,便牽了手,不必緊握,卻永不放松,以自己設(shè)計的愛的程式,去演繹一種精典的永恒。

            經(jīng)典語錄11: 

            我們之所以會心累,就是常常徘徊在堅持和放棄之間,舉棋不定。我們之所以會煩惱,就是記性太好,該記的,不該記的都會留在記憶里。我們之所以會痛苦,就是追求的太多。我們之所以不快樂,就是計較的太多,不是我們擁有的太少,而是我們計較的太多。

            經(jīng)典語錄12: 

            男人吸引女人的10個特質(zhì):1.真實 2.深刻 3.胸懷 4.敢為 5.風(fēng)度 6.機靈 7.幽默 8.進取 9.浪漫 10.冒險.女人吸引男人的10個特點:1.溫柔 2.知性 3.直性 4.涵養(yǎng) 5.朦朧 6.小動作 7.勤于家事 8.膚白 9.性感著裝 10.香氛

            經(jīng)典語錄13: 

            真正的愛,是接受,不是忍受;是支持,不是支配;是慰問,不是質(zhì)問;真正的愛,要道謝也要道歉。要體貼,也要體諒。要認錯,也好改錯;真正的愛,不是彼此凝視,而是共同沿著同一方向望去。其實,愛不是尋找一個完美的人。而是,要學(xué)會用完美的眼光,欣賞一個并不完美的人。

            經(jīng)典語錄14: 

            身邊總有些人,你看見他整天都開心,率真得像個小孩,人人都羨慕他;其實,你哪里知道:前一秒人后還傷心地流著淚的他,后一秒人前即刻洋溢燦爛笑容。他們其實沒有能力獨處,夜深人靜時,總坐在窗前對著夜空冥想失意的苦楚。他們就像向日葵,向著太陽的正面永遠明媚鮮亮,在照不到的背面卻將悲傷深藏。

            經(jīng)典語錄15: 

            生命中,有些人來了又去,有些人去而復(fù)返,有些人近在咫尺,有些人遠在天涯,有些人擦身而過,有些人一路同行。或許在某兩條路的盡頭相遇,結(jié)伴同行了一段路程,又在下一個分岔路口道別。無論如何,終免不了曲終人散的傷感。遠在天涯的朋友:或許已是遙遠得無法問候,但還是謝謝您曾經(jīng)的結(jié)伴同行。

            經(jīng)典語錄16: 

            愛情很簡單,因為每個人都會說:“我愛你,會為你付出一切!”,愛情很難,因為沒有多少人做到了他的承諾。如果真心愛一個人,不承諾也會去愛;如果不愛一個人,曾經(jīng)承諾也會背叛。

            經(jīng)典語錄17: 

            【你最后悔什么】某雜志對全國60歲以上的老人抽樣調(diào)查:第一名:75%的人后悔年輕時努力不夠,導(dǎo)致一事無成。第二名:70%的人后悔在年輕的時候選錯了職業(yè)。第三名:62%的人后悔對子女教育不當(dāng)。第四名:57%的人后悔沒有好好珍惜自己的伴侶。第五名:49%的人后悔沒有善待自己的身體。

            經(jīng)典語錄18: 

            【做人十心機】⒈做人不能太單純 適度偽裝自己 ⒉凡事留余地 要留退路 ⒊話不說絕口無遮攔難成大事 ⒋成熟而不世故 ⒌心態(tài)好 想得開活得不累 ⒍懂方圓之道:沒事不惹事,來事不怕事 ⒎不可少二禮:禮儀與禮物 ⒏人在江湖飄 防挨朋友刀 ⒐偶爾"勢利眼" 尋可靠伙伴 ⒑放下面子來做人。

            經(jīng)典語錄19: 

            人生旅途中,總有人不斷地走來,有人不斷地離去。當(dāng)新的名字變成老的名字,當(dāng)老的名字漸漸模糊,又是一個故事的結(jié)束和另一個故事的開始。在不斷的相遇和錯開中,終于明白:身邊的人只能陪著自己走過或近或遠的一程,而不能伴自己一生;陪伴一生的是自己的名字和那些或清晰或模糊的名字所帶來的感動。

            經(jīng)典語錄20: 

            從現(xiàn)在開始,聰明一點,不要問別人想不想你,愛不愛你?若是要想你或者愛你自然會對你說,但是從你的嘴里說出來,別人會很驕傲和不在乎你。再也不要太在意一些人,太在乎一些事,順其自然以最佳心態(tài)面對,因為這個世界就是這樣:往往在最在乎的事物面前,我們最沒有價值。

            經(jīng)典語錄21: 

            一個人的成就,不是以金錢衡量,而是一生中,你善待過多少人,有多少人懷念你。生意人的賬簿,記錄收入與支出,兩數(shù)相減,便是盈利。人生的賬簿,記錄愛與被愛,兩數(shù)相加,就是成就。
            posted @ 2011-05-09 17:57 小果子 閱讀(186) | 評論 (0)編輯 收藏

            引言

            本書主要針對的是 C++ 程序的性能優(yōu)化,深入介紹 C++ 程序性能優(yōu)化的方法和實例。全書由 4 個篇組成,第 1 篇介紹 C++ 語言的對象模型,該篇是優(yōu)化 C++ 程序的基礎(chǔ);第 2 篇主要針對如何優(yōu)化 C++ 程序的內(nèi)存使用;第 3 篇介紹如何優(yōu)化程序的啟動性能;第 4 篇介紹了三類性能優(yōu)化工具,即內(nèi)存分析工具、性能分析工具和 I/O 檢測工具,它們是測量程序性能的利器。

            本章首先簡單介紹自定義內(nèi)存池性能優(yōu)化的原理,然后列舉軟件開發(fā)中常用的內(nèi)存池的不同類型,并給出具體實現(xiàn)的實例。

            6.1 自定義內(nèi)存池性能優(yōu)化的原理

             

            圖書信息 書名:《C++應(yīng)用程序性能優(yōu)化》
            作者:馮宏華、徐瑩、程遠、汪磊 等編著
            出版社:電子工業(yè)出版社
            出版日期:2007 年 03 月
            ISBN:978-7-121-03831-0
            購買: 中國互動出版網(wǎng)、dearbook

            推薦章節(jié):

             

            更多推薦書籍,請訪問 developerWorks 圖書頻道。

            歡迎您對本書提出寶貴的反饋意見。您可以通過本頁面最下方的 建議 欄目為本文打分,并反饋您的建議和意見。

            如果您對 developerWorks 圖書頻道有什么好的建議,歡迎您將建議發(fā)給我們。

             

            如前所述,讀者已經(jīng)了解到"堆"和"棧"的區(qū)別。而在編程實踐中,不可避免地要大量用到堆上的內(nèi)存。例如在程序中維護一個鏈表的數(shù)據(jù)結(jié)構(gòu)時,每次新增或者刪除一個鏈表的節(jié)點,都需要從內(nèi)存堆上分配或者釋放一定的內(nèi)存;在維護一個動態(tài)數(shù)組時,如果動態(tài)數(shù)組的大小不能滿足程序需要時,也要在內(nèi)存堆上分配新的內(nèi)存空間。

            6.1.1 默認內(nèi)存管理函數(shù)的不足

            利用默認的內(nèi)存管理函數(shù)new/delete或malloc/free在堆上分配和釋放內(nèi)存會有一些額外的開銷。

            系統(tǒng)在接收到分配一定大小內(nèi)存的請求時,首先查找內(nèi)部維護的內(nèi)存空閑塊表,并且需要根據(jù)一定的算法(例如分配最先找到的不小于申請大小的內(nèi)存塊給請求者,或者分配最適于申請大小的內(nèi)存塊,或者分配最大空閑的內(nèi)存塊等)找到合適大小的空閑內(nèi)存塊。如果該空閑內(nèi)存塊過大,還需要切割成已分配的部分和較小的空閑塊。然后系統(tǒng)更新內(nèi)存空閑塊表,完成一次內(nèi)存分配。類似地,在釋放內(nèi)存時,系統(tǒng)把釋放的內(nèi)存塊重新加入到空閑內(nèi)存塊表中。如果有可能的話,可以把相鄰的空閑塊合并成較大的空閑塊。

            默認的內(nèi)存管理函數(shù)還考慮到多線程的應(yīng)用,需要在每次分配和釋放內(nèi)存時加鎖,同樣增加了開銷。

            可見,如果應(yīng)用程序頻繁地在堆上分配和釋放內(nèi)存,則會導(dǎo)致性能的損失。并且會使系統(tǒng)中出現(xiàn)大量的內(nèi)存碎片,降低內(nèi)存的利用率。

            默認的分配和釋放內(nèi)存算法自然也考慮了性能,然而這些內(nèi)存管理算法的通用版本為了應(yīng)付更復(fù)雜、更廣泛的情況,需要做更多的額外工作。而對于某一個具體的應(yīng)用程序來說,適合自身特定的內(nèi)存分配釋放模式的自定義內(nèi)存池則可以獲得更好的性能。

            6.1.2 內(nèi)存池的定義和分類

            自定義內(nèi)存池的思想通過這個"池"字表露無疑,應(yīng)用程序可以通過系統(tǒng)的內(nèi)存分配調(diào)用預(yù)先一次性申請適當(dāng)大小的內(nèi)存作為一個內(nèi)存池,之后應(yīng)用程序自己對內(nèi)存的分配和釋放則可以通過這個內(nèi)存池來完成。只有當(dāng)內(nèi)存池大小需要動態(tài)擴展時,才需要再調(diào)用系統(tǒng)的內(nèi)存分配函數(shù),其他時間對內(nèi)存的一切操作都在應(yīng)用程序的掌控之中。

            應(yīng)用程序自定義的內(nèi)存池根據(jù)不同的適用場景又有不同的類型。

            從線程安全的角度來分,內(nèi)存池可以分為單線程內(nèi)存池和多線程內(nèi)存池。單線程內(nèi)存池整個生命周期只被一個線程使用,因而不需要考慮互斥訪問的問題;多線程內(nèi)存池有可能被多個線程共享,因此則需要在每次分配和釋放內(nèi)存時加鎖。相對而言,單線程內(nèi)存池性能更高,而多線程內(nèi)存池適用范圍更廣。

            從內(nèi)存池可分配內(nèi)存單元大小來分,可以分為固定內(nèi)存池和可變內(nèi)存池。所謂固定內(nèi)存池是指應(yīng)用程序每次從內(nèi)存池中分配出來的內(nèi)存單元大小事先已經(jīng)確定,是固定不變的;而可變內(nèi)存池則每次分配的內(nèi)存單元大小可以按需變化,應(yīng)用范圍更廣,而性能比固定內(nèi)存池要低。

            6.1.3 內(nèi)存池工作原理示例

            下面以固定內(nèi)存池為例說明內(nèi)存池的工作原理,如圖6-1所示。


            圖6-1 固定內(nèi)存池
            圖6-1  固定內(nèi)存池

            固定內(nèi)存池由一系列固定大小的內(nèi)存塊組成,每一個內(nèi)存塊又包含了固定數(shù)量和大小的內(nèi)存單元。

            如圖6-1所示,該內(nèi)存池一共包含4個內(nèi)存塊。在內(nèi)存池初次生成時,只向系統(tǒng)申請了一個內(nèi)存塊,返回的指針作為整個內(nèi)存池的頭指針。之后隨著應(yīng)用程序?qū)?nèi)存的不斷需求,內(nèi)存池判斷需要動態(tài)擴大時,才再次向系統(tǒng)申請新的內(nèi)存塊,并把所有這些內(nèi)存塊通過指針鏈接起來。對于操作系統(tǒng)來說,它已經(jīng)為該應(yīng)用程序分配了4個等大小的內(nèi)存塊。由于是大小固定的,所以分配的速度比較快;而對于應(yīng)用程序來說,其內(nèi)存池開辟了一定大小,內(nèi)存池內(nèi)部卻還有剩余的空間。

            例如放大來看第4個內(nèi)存塊,其中包含一部分內(nèi)存池塊頭信息和3個大小相等的內(nèi)存池單元。單元1和單元3是空閑的,單元2已經(jīng)分配。當(dāng)應(yīng)用程序需要通過該內(nèi)存池分配一個單元大小的內(nèi)存時,只需要簡單遍歷所有的內(nèi)存池塊頭信息,快速定位到還有空閑單元的那個內(nèi)存池塊。然后根據(jù)該塊的塊頭信息直接定位到第1個空閑的單元地址,把這個地址返回,并且標記下一個空閑單元即可;當(dāng)應(yīng)用程序釋放某一個內(nèi)存池單元時,直接在對應(yīng)的內(nèi)存池塊頭信息中標記該內(nèi)存單元為空閑單元即可。

            可見與系統(tǒng)管理內(nèi)存相比,內(nèi)存池的操作非常迅速,它在性能優(yōu)化方面的優(yōu)點主要如下。

            (1)針對特殊情況,例如需要頻繁分配釋放固定大小的內(nèi)存對象時,不需要復(fù)雜的分配算法和多線程保護。也不需要維護內(nèi)存空閑表的額外開銷,從而獲得較高的性能。

            (2)由于開辟一定數(shù)量的連續(xù)內(nèi)存空間作為內(nèi)存池塊,因而一定程度上提高了程序局部性,提升了程序性能。

            (3)比較容易控制頁邊界對齊和內(nèi)存字節(jié)對齊,沒有內(nèi)存碎片的問題。

            6.2 一個內(nèi)存池的實現(xiàn)實例

            本節(jié)分析在某個大型應(yīng)用程序?qū)嶋H應(yīng)用到的一個內(nèi)存池實現(xiàn),并詳細講解其使用方法與工作原理。這是一個應(yīng)用于單線程環(huán)境且分配單元大小固定的內(nèi)存池,一般用來為執(zhí)行時會動態(tài)頻繁地創(chuàng)建且可能會被多次創(chuàng)建的類對象或者結(jié)構(gòu)體分配內(nèi)存。

            本節(jié)首先講解該內(nèi)存池的數(shù)據(jù)結(jié)構(gòu)聲明及圖示,接著描述其原理及行為特征。然后逐一講解實現(xiàn)細節(jié),最后介紹如何在實際程序中應(yīng)用此內(nèi)存池,并與使用普通內(nèi)存函數(shù)申請內(nèi)存的程序性能作比較。

            6.2.1 內(nèi)部構(gòu)造

            內(nèi)存池類MemoryPool的聲明如下:


            class MemoryPool
                        {
                        private:
                        MemoryBlock*   pBlock;
                        USHORT          nUnitSize;
                        USHORT          nInitSize;
                        USHORT          nGrowSize;
                        public:
                        MemoryPool( USHORT nUnitSize,
                        USHORT nInitSize = 1024,
                        USHORT nGrowSize = 256 );
                        ~MemoryPool();
                        void*           Alloc();
                        void            Free( void* p );
                        };
                        

            MemoryBlock為內(nèi)存池中附著在真正用來為內(nèi)存請求分配內(nèi)存的內(nèi)存塊頭部的結(jié)構(gòu)體,它描述了與之聯(lián)系的內(nèi)存塊的使用信息:


            struct MemoryBlock
                        {
                        USHORT          nSize;
                        USHORT          nFree;
                        USHORT          nFirst;
                        USHORT          nDummyAlign1;
                        MemoryBlock*  pNext;
                        char            aData[1];
                        static void* operator new(size_t, USHORT nTypes, USHORT nUnitSize)
                        {
                        return ::operator new(sizeof(MemoryBlock) + nTypes * nUnitSize);
                        }
                        static void  operator delete(void *p, size_t)
                        {
                        ::operator delete (p);
                        }
                        MemoryBlock (USHORT nTypes = 1, USHORT nUnitSize = 0);
                        ~MemoryBlock() {}
                        };
                        

            此內(nèi)存池的數(shù)據(jù)結(jié)構(gòu)如圖6-2所示。


            圖6-2 內(nèi)存池的數(shù)據(jù)結(jié)構(gòu)
            圖6-2  內(nèi)存池的數(shù)據(jù)結(jié)構(gòu)

            6.2.2 總體機制

            此內(nèi)存池的總體機制如下。

            (1)在運行過程中,MemoryPool內(nèi)存池可能會有多個用來滿足內(nèi)存申請請求的內(nèi)存塊,這些內(nèi)存塊是從進程堆中開辟的一個較大的連續(xù)內(nèi)存區(qū)域,它由一個MemoryBlock結(jié)構(gòu)體和多個可供分配的內(nèi)存單元組成,所有內(nèi)存塊組成了一個內(nèi)存塊鏈表,MemoryPool 的pBlock是這個鏈表的頭。對每個內(nèi)存塊,都可以通過其頭部的MemoryBlock結(jié)構(gòu)體的pNext成員訪問緊跟在其后面的那個內(nèi)存塊。

            (2)每個內(nèi)存塊由兩部分組成,即一個MemoryBlock結(jié)構(gòu)體和多個內(nèi)存分配單元。這些內(nèi)存分配單元大小固定(由 MemoryPool的nUnitSize表示),MemoryBlock結(jié)構(gòu)體并不維護那些已經(jīng)分配的單元的信息;相反,它只維護沒有分配的自由分配單元的信息。它有兩個成員比較重要:nFree和nFirst。nFree記錄這個內(nèi)存塊中還有多少個自由分配單元,而nFirst則記錄下一個可供分配的單元的編號。每一個自由分配單元的頭兩個字節(jié)(即一個USHORT型值)記錄了緊跟它之后的下一個自由分配單元的編號,這樣,通過利用每個自由分配單元的頭兩個字節(jié),一個MemoryBlock中的所有自由分配單元被鏈接起來。

            (3)當(dāng)有新的內(nèi)存請求到來時,MemoryPool會通過pBlock遍歷MemoryBlock鏈表,直到找到某個 MemoryBlock所在的內(nèi)存塊,其中還有自由分配單元(通過檢測MemoryBlock結(jié)構(gòu)體的nFree成員是否大于0)。如果找到這樣的內(nèi)存塊,取得其MemoryBlock的nFirst值(此為該內(nèi)存塊中第1個可供分配的自由單元的編號)。然后根據(jù)這個編號定位到該自由分配單元的起始位置(因為所有分配單元大小固定,因此每個分配單元的起始位置都可以通過編號分配單元大小來偏移定位),這個位置就是用來滿足此次內(nèi)存申請請求的內(nèi)存的起始地址。但在返回這個地址前,需要首先將該位置開始的頭兩個字節(jié)的值(這兩個字節(jié)值記錄其之后的下一個自由分配單元的編號)賦給本內(nèi)存塊的 MemoryBlock的nFirst成員。這樣下一次的請求就會用這個編號對應(yīng)的內(nèi)存單元來滿足,同時將此內(nèi)存塊的MemoryBlock的nFree 遞減1,然后才將剛才定位到的內(nèi)存單元的起始位置作為此次內(nèi)存請求的返回地址返回給調(diào)用者。

            (4)如果從現(xiàn)有的內(nèi)存塊中找不到一個自由的內(nèi)存分配單元(當(dāng)?shù)?次請求內(nèi)存,以及現(xiàn)有的所有內(nèi)存塊中的所有內(nèi)存分配單元都已經(jīng)被分配時會發(fā)生這種情形),MemoryPool就會從進程堆中申請一個內(nèi)存塊(這個內(nèi)存塊包括一個MemoryBlock結(jié)構(gòu)體,及緊鄰其后的多個內(nèi)存分配單元,假設(shè)內(nèi)存分配單元的個數(shù)為n,n可以取值MemoryPool中的nInitSize或者nGrowSize),申請完后,并不會立刻將其中的一個分配單元分配出去,而是需要首先初始化這個內(nèi)存塊。初始化的操作包括設(shè)置MemoryBlock的nSize為所有內(nèi)存分配單元的大?。ㄗ⒁?,并不包括MemoryBlock結(jié)構(gòu)體的大?。?、nFree為n-1(注意,這里是n-1而不是n,因為此次新內(nèi)存塊就是為了滿足一次新的內(nèi)存請求而申請的,馬上就會分配一塊自由存儲單元出去,如果設(shè)為n-1,分配一個自由存儲單元后無須再將n遞減1),nFirst為1(已經(jīng)知道nFirst為下一個可以分配的自由存儲單元的編號。為1的原因與nFree為n-1相同,即立即會將編號為0的自由分配單元分配出去?,F(xiàn)在設(shè)為1,其后不用修改nFirst的值),MemoryBlock的構(gòu)造需要做更重要的事情,即將編號為0的分配單元之后的所有自由分配單元鏈接起來。如前所述,每個自由分配單元的頭兩個字節(jié)用來存儲下一個自由分配單元的編號。另外,因為每個分配單元大小固定,所以可以通過其編號和單元大小(MemoryPool的nUnitSize成員)的乘積作為偏移值進行定位。現(xiàn)在唯一的問題是定位從哪個地址開始?答案是MemoryBlock的aData[1]成員開始。因為aData[1]實際上是屬于MemoryBlock結(jié)構(gòu)體的(MemoryBlock結(jié)構(gòu)體的最后一個字節(jié)),所以實質(zhì)上,MemoryBlock結(jié)構(gòu)體的最后一個字節(jié)也用做被分配出去的分配單元的一部分。因為整個內(nèi)存塊由MemoryBlock結(jié)構(gòu)體和整數(shù)個分配單元組成,這意味著內(nèi)存塊的最后一個字節(jié)會被浪費,這個字節(jié)在圖6-2中用位于兩個內(nèi)存的最后部分的濃黑背景的小塊標識。確定了分配單元的起始位置后,將自由分配單元鏈接起來的工作就很容易了。即從aData位置開始,每隔nUnitSize大小取其頭兩個字節(jié),記錄其之后的自由分配單元的編號。因為剛開始所有分配單元都是自由的,所以這個編號就是自身編號加1,即位置上緊跟其后的單元的編號。初始化后,將此內(nèi)存塊的第1個分配單元的起始地址返回,已經(jīng)知道這個地址就是aData。

            (5)當(dāng)某個被分配的單元因為delete需要回收時,該單元并不會返回給進程堆,而是返回給MemoryPool。返回時,MemoryPool能夠知道該單元的起始地址。這時,MemoryPool開始遍歷其所維護的內(nèi)存塊鏈表,判斷該單元的起始地址是否落在某個內(nèi)存塊的地址范圍內(nèi)。如果不在所有內(nèi)存地址范圍內(nèi),則這個被回收的單元不屬于這個MemoryPool;如果在某個內(nèi)存塊的地址范圍內(nèi),那么它會將這個剛剛回收的分配單元加到這個內(nèi)存塊的MemoryBlock所維護的自由分配單元鏈表的頭部,同時將其nFree值遞增1。回收后,考慮到資源的有效利用及后續(xù)操作的性能,內(nèi)存池的操作會繼續(xù)判斷:如果此內(nèi)存塊的所有分配單元都是自由的,那么這個內(nèi)存塊就會從MemoryPool中被移出并作為一個整體返回給進程堆;如果該內(nèi)存塊中還有非自由分配單元,這時不能將此內(nèi)存塊返回給進程堆。但是因為剛剛有一個分配單元返回給了這個內(nèi)存塊,即這個內(nèi)存塊有自由分配單元可供下次分配,因此它會被移到MemoryPool維護的內(nèi)存塊的頭部。這樣下次的內(nèi)存請求到來,MemoryPool遍歷其內(nèi)存塊鏈表以尋找自由分配單元時,第1次尋找就會找到這個內(nèi)存塊。因為這個內(nèi)存塊確實有自由分配單元,這樣可以減少MemoryPool的遍歷次數(shù)。

            綜上所述,每個內(nèi)存池(MemoryPool)維護一個內(nèi)存塊鏈表(單鏈表),每個內(nèi)存塊由一個維護該內(nèi)存塊信息的塊頭結(jié)構(gòu)(MemoryBlock)和多個分配單元組成,塊頭結(jié)構(gòu)MemoryBlock則進一步維護一個該內(nèi)存塊的所有自由分配單元組成的"鏈表"。這個鏈表不是通過"指向下一個自由分配單元的指針"鏈接起來的,而是通過"下一個自由分配單元的編號"鏈接起來,這個編號值存儲在該自由分配單元的頭兩個字節(jié)中。另外,第1個自由分配單元的起始位置并不是MemoryBlock結(jié)構(gòu)體"后面的"第1個地址位置,而是MemoryBlock結(jié)構(gòu)體"內(nèi)部"的最后一個字節(jié)aData(也可能不是最后一個,因為考慮到字節(jié)對齊的問題),即分配單元實際上往前面錯了一位。又因為MemoryBlock結(jié)構(gòu)體后面的空間剛好是分配單元的整數(shù)倍,這樣依次錯位下去,內(nèi)存塊的最后一個字節(jié)實際沒有被利用。這么做的一個原因也是考慮到不同平臺的移植問題,因為不同平臺的對齊方式可能不盡相同。即當(dāng)申請MemoryBlock大小內(nèi)存時,可能會返回比其所有成員大小總和還要大一些的內(nèi)存。最后的幾個字節(jié)是為了"補齊",而使得 aData成為第1個分配單元的起始位置,這樣在對齊方式不同的各種平臺上都可以工作。

            6.2.3 細節(jié)剖析

            有了上述的總體印象后,本節(jié)來仔細剖析其實現(xiàn)細節(jié)。

            (1)MemoryPool的構(gòu)造如下:


            MemoryPool::MemoryPool( USHORT _nUnitSize,
                        USHORT _nInitSize, USHORT _nGrowSize )
                        {
                        pBlock      = NULL;	            ①
                        nInitSize   = _nInitSize;       ②
                        nGrowSize   = _nGrowSize;       ③
                        if ( _nUnitSize > 4 )
                        nUnitSize = (_nUnitSize + (MEMPOOL_ALIGNMENT-1)) & ~(MEMPOOL_ALIGNMENT-1); ④
                        else if ( _nUnitSize <= 2 )
                        nUnitSize = 2;              ⑤
                        else
                        nUnitSize = 4;
                        }
                        

            從①處可以看出,MemoryPool創(chuàng)建時,并沒有立刻創(chuàng)建真正用來滿足內(nèi)存申請的內(nèi)存塊,即內(nèi)存塊鏈表剛開始時為空。

            ②處和③處分別設(shè)置"第1次創(chuàng)建的內(nèi)存塊所包含的分配單元的個數(shù)",及"隨后創(chuàng)建的內(nèi)存塊所包含的分配單元的個數(shù)",這兩個值在MemoryPool創(chuàng)建時通過參數(shù)指定,其后在該MemoryPool對象生命周期中一直不變。

            后面的代碼用來設(shè)置nUnitSize,這個值參考傳入的_nUnitSize參數(shù)。但是還需要考慮兩個因素。如前所述,每個分配單元在自由狀態(tài)時,其頭兩個字節(jié)用來存放"其下一個自由分配單元的編號"。即每個分配單元"最少"有"兩個字節(jié)",這就是⑤處賦值的原因。④處是將大于4個字節(jié)的大小_nUnitSize往上"取整到"大于_nUnitSize的最小的MEMPOOL_ ALIGNMENT的倍數(shù)(前提是MEMPOOL_ALIGNMENT為2的倍數(shù))。如_nUnitSize為11 時,MEMPOOL_ALIGNMENT為8,nUnitSize為16;MEMPOOL_ALIGNMENT為4,nUnitSize為 12;MEMPOOL_ALIGNMENT為2,nUnitSize為12,依次類推。

            (2)當(dāng)向MemoryPool提出內(nèi)存請求時:


            void* MemoryPool::Alloc()
                        {
                        if ( !pBlock )           ①
                        {
                        ……
                        }
                        MemoryBlock* pMyBlock = pBlock;
                        while (pMyBlock && !pMyBlock->nFree )②
                        pMyBlock = pMyBlock->pNext;
                        if ( pMyBlock )	         ③
                        {
                        char* pFree = pMyBlock->aData+(pMyBlock->nFirst*nUnitSize);
                        pMyBlock->nFirst = *((USHORT*)pFree);
                        pMyBlock->nFree--;
                        return (void*)pFree;
                        }
                        else                    ④
                        {
                        if ( !nGrowSize )
                        return NULL;
                        pMyBlock = new(nGrowSize, nUnitSize) FixedMemBlock(nGrowSize, nUnitSize);
                        if ( !pMyBlock )
                        return NULL;
                        pMyBlock->pNext = pBlock;
                        pBlock = pMyBlock;
                        return (void*)(pMyBlock->aData);
                        }
                        }
                        

            MemoryPool滿足內(nèi)存請求的步驟主要由四步組成。

            ①處首先判斷內(nèi)存池當(dāng)前內(nèi)存塊鏈表是否為空,如果為空,則意味著這是第1次內(nèi)存申請請求。這時,從進程堆中申請一個分配單元個數(shù)為nInitSize的內(nèi)存塊,并初始化該內(nèi)存塊(主要初始化MemoryBlock結(jié)構(gòu)體成員,以及創(chuàng)建初始的自由分配單元鏈表,下面會詳細分析其代碼)。如果該內(nèi)存塊申請成功,并初始化完畢,返回第1個分配單元給調(diào)用函數(shù)。第1個分配單元以MemoryBlock結(jié)構(gòu)體內(nèi)的最后一個字節(jié)為起始地址。

            ②處的作用是當(dāng)內(nèi)存池中已有內(nèi)存塊(即內(nèi)存塊鏈表不為空)時遍歷該內(nèi)存塊鏈表,尋找還有"自由分配單元"的內(nèi)存塊。

            ③處檢查如果找到還有自由分配單元的內(nèi)存塊,則"定位"到該內(nèi)存塊現(xiàn)在可以用的自由分配單元處。"定位"以 MemoryBlock結(jié)構(gòu)體內(nèi)的最后一個字節(jié)位置aData為起始位置,以MemoryPool的nUnitSize為步長來進行。找到后,需要修改 MemoryBlock的nFree信息(剩下來的自由分配單元比原來減少了一個),以及修改此內(nèi)存塊的自由存儲單元鏈表的信息。在找到的內(nèi)存塊中,pMyBlock->nFirst為該內(nèi)存塊中自由存儲單元鏈表的表頭,其下一個自由存儲單元的編號存放在 pMyBlock->nFirst指示的自由存儲單元(亦即剛才定位到的自由存儲單元)的頭兩個字節(jié)。通過剛才定位到的位置,取其頭兩個字節(jié)的值,賦給pMyBlock->nFirst,這就是此內(nèi)存塊的自由存儲單元鏈表的新的表頭,即下一次分配出去的自由分配單元的編號(如果nFree大于零的話)。修改維護信息后,就可以將剛才定位到的自由分配單元的地址返回給此次申請的調(diào)用函數(shù)。注意,因為這個分配單元已經(jīng)被分配,而內(nèi)存塊無須維護已分配的分配單元,因此該分配單元的頭兩個字節(jié)的信息已經(jīng)沒有用處。換個角度看,這個自由分配單元返回給調(diào)用函數(shù)后,調(diào)用函數(shù)如何處置這塊內(nèi)存,內(nèi)存池?zé)o從知曉,也無須知曉。此分配單元在返回給調(diào)用函數(shù)時,其內(nèi)容對于調(diào)用函數(shù)來說是無意義的。因此幾乎可以肯定調(diào)用函數(shù)在用這個單元的內(nèi)存時會覆蓋其原來的內(nèi)容,即頭兩個字節(jié)的內(nèi)容也會被抹去。因此每個存儲單元并沒有因為需要鏈接而引入多余的維護信息,而是直接利用單元內(nèi)的頭兩個字節(jié),當(dāng)其分配后,頭兩個字節(jié)也可以被調(diào)用函數(shù)利用。而在自由狀態(tài)時,則用來存放維護信息,即下一個自由分配單元的編號,這是一個有效利用內(nèi)存的好例子。

            ④處表示在②處遍歷時,沒有找到還有自由分配單元的內(nèi)存塊,這時,需要重新向進程堆申請一個內(nèi)存塊。因為不是第一次申請內(nèi)存塊,所以申請的內(nèi)存塊包含的分配單元個數(shù)為nGrowSize,而不再是nInitSize。與①處相同,先做這個新申請內(nèi)存塊的初始化工作,然后將此內(nèi)存塊插入MemoryPool的內(nèi)存塊鏈表的頭部,再將此內(nèi)存塊的第1個分配單元返回給調(diào)用函數(shù)。將此新內(nèi)存塊插入內(nèi)存塊鏈表的頭部的原因是該內(nèi)存塊還有很多可供分配的自由分配單元(除非nGrowSize等于1,這應(yīng)該不太可能。因為內(nèi)存池的含義就是一次性地從進程堆中申請一大塊內(nèi)存,以供后續(xù)的多次申請),放在頭部可以使得在下次收到內(nèi)存申請時,減少②處對內(nèi)存塊的遍歷時間。

            可以用圖6-2的MemoryPool來展示MemoryPool::Alloc的過程。圖6-3是某個時刻MemoryPool的內(nèi)部狀態(tài)。


            圖6-3 某個時刻MemoryPool的內(nèi)部狀態(tài)
            圖6-3  某個時刻MemoryPool的內(nèi)部狀態(tài)

            因為MemoryPool的內(nèi)存塊鏈表不為空,因此會遍歷其內(nèi)存塊鏈表。又因為第1個內(nèi)存塊里有自由的分配單元,所以會從第1個內(nèi)存塊中分配。檢查nFirst,其值為m,這時pBlock->aData+(pBlock->nFirst*nUnitSize) 定位到編號為m的自由分配單元的起始位置(用pFree表示)。在返回pFree之前,需要修改此內(nèi)存塊的維護信息。首先將nFree遞減1,然后取得 pFree處開始的頭兩個字節(jié)的值(需要說明的是,這里aData處值為k。其實不是這一個字節(jié)。而是以aData和緊跟其后的另外一個字節(jié)合在一起構(gòu)成的一個USHORT的值,不可誤會)。發(fā)現(xiàn)為k,這時修改pBlock的nFirst為k。然后,返回pFree。此時MemoryPool的結(jié)構(gòu)如圖 6-4所示。


            圖6-4 MemoryPool的結(jié)構(gòu)
            圖6-4  MemoryPool的結(jié)構(gòu)

            可以看到,原來的第1個可供分配的單元(m編號處)已經(jīng)顯示為被分配的狀態(tài)。而pBlock的nFirst已經(jīng)指向原來m單元下一個自由分配單元的編號,即k。

            (3)MemoryPool回收內(nèi)存時:


            void MemoryPool::Free( void* pFree )
                        {
                        ……
                        MemoryBlock* pMyBlock = pBlock;
                        while ( ((ULONG)pMyBlock->aData > (ULONG)pFree) ||
                        ((ULONG)pFree >= ((ULONG)pMyBlock->aData + pMyBlock->nSize)) )①
                        {
                        ……
                        }
                        pMyBlock->nFree++;                     ②
                        *((USHORT*)pFree) = pMyBlock->nFirst;  ③
                        pMyBlock->nFirst = (USHORT)(((ULONG)pFree-(ULONG)(pBlock->aData)) / nUnitSize);④
                        if (pMyBlock->nFree*nUnitSize == pMyBlock->nSize )⑤
                        {
                        ……
                        }
                        else
                        {
                        ……
                        }
                        }
                        

            如前所述,回收分配單元時,可能會將整個內(nèi)存塊返回給進程堆,也可能將被回收分配單元所屬的內(nèi)存塊移至內(nèi)存池的內(nèi)存塊鏈表的頭部。這兩個操作都需要修改鏈表結(jié)構(gòu)。這時需要知道該內(nèi)存塊在鏈表中前一個位置的內(nèi)存塊。

            ①處遍歷內(nèi)存池的內(nèi)存塊鏈表,確定該待回收分配單元(pFree)落在哪一個內(nèi)存塊的指針范圍內(nèi),通過比較指針值來確定。

            運行到②處,pMyBlock即找到的包含pFree所指向的待回收分配單元的內(nèi)存塊(當(dāng)然,這時應(yīng)該還需要檢查 pMyBlock為NULL時的情形,即pFree不屬于此內(nèi)存池的范圍,因此不能返回給此內(nèi)存池,讀者可以自行加上)。這時將pMyBlock的 nFree遞增1,表示此內(nèi)存塊的自由分配單元多了一個。

            ③處用來修改該內(nèi)存塊的自由分配單元鏈表的信息,它將這個待回收分配單元的頭兩個字節(jié)的值指向該內(nèi)存塊原來的第一個可分配的自由分配單元的編號。

            ④處將pMyBlock的nFirst值改變?yōu)橹赶蜻@個待回收分配單元的編號,其編號通過計算此單元的起始位置相對pMyBlock的aData位置的差值,然后除以步長(nUnitSize)得到。

            實質(zhì)上,③和④兩步的作用就是將此待回收分配單元"真正回收"。值得注意的是,這兩步實際上是使得此回收單元成為此內(nèi)存塊的下一個可分配的自由分配單元,即將它放在了自由分配單元鏈表的頭部。注意,其內(nèi)存地址并沒有發(fā)生改變。實際上,一個分配單元的內(nèi)存地址無論是在分配后,還是處于自由狀態(tài)時,一直都不會變化。變化的只是其狀態(tài)(已分配/自由),以及當(dāng)其處于自由狀態(tài)時在自由分配單元鏈表中的位置。

            ⑤處檢查當(dāng)回收完畢后,包含此回收單元的內(nèi)存塊的所有單元是否都處于自由狀態(tài),且此內(nèi)存是否處于內(nèi)存塊鏈表的頭部。如果是,將此內(nèi)存塊整個的返回給進程堆,同時修改內(nèi)存塊鏈表結(jié)構(gòu)。

            注意,這里在判斷一個內(nèi)存塊的所有單元是否都處于自由狀態(tài)時,并沒有遍歷其所有單元,而是判斷nFree乘以 nUnitSize是否等于nSize。nSize是內(nèi)存塊中所有分配單元的大小,而不包括頭部MemoryBlock結(jié)構(gòu)體的大小。這里可以看到其用意,即用來快速檢查某個內(nèi)存塊中所有分配單元是否全部處于自由狀態(tài)。因為只需結(jié)合nFree和nUnitSize來計算得出結(jié)論,而無須遍歷和計算所有自由狀態(tài)的分配單元的個數(shù)。

            另外還需注意的是,這里并不能比較nFree與nInitSize或nGrowSize的大小來判斷某個內(nèi)存塊中所有分配單元都為自由狀態(tài),這是因為第1次分配的內(nèi)存塊(分配單元個數(shù)為nInitSize)可能被移到鏈表的后面,甚至可能在移到鏈表后面后,因為某個時間其所有單元都處于自由狀態(tài)而被整個返回給進程堆。即在回收分配單元時,無法判定某個內(nèi)存塊中的分配單元個數(shù)到底是nInitSize還是nGrowSize,也就無法通過比較nFree與nInitSize或nGrowSize的大小來判斷一個內(nèi)存塊的所有分配單元是否都為自由狀態(tài)。

            以上面分配后的內(nèi)存池狀態(tài)作為例子,假設(shè)這時第2個內(nèi)存塊中的最后一個單元需要回收(已被分配,假設(shè)其編號為m,pFree指針指向它),如圖6-5所示。

            不難發(fā)現(xiàn),這時nFirst的值由原來的0變?yōu)閙。即此內(nèi)存塊下一個被分配的單元是m編號的單元,而不是0編號的單元(最先分配的是最新回收的單元,從這一點看,這個過程與棧的原理類似,即先進后出。只不過這里的"進"意味著"回收",而"出"則意味著"分配")。相應(yīng)地,m的"下一個自由單元"標記為0,即內(nèi)存塊原來的"下一個將被分配出去的單元",這也表明最近回收的分配單元被插到了內(nèi)存塊的"自由分配單元鏈表"的頭部。當(dāng)然,nFree遞增1。


            圖6-5 分配后的內(nèi)存池狀態(tài)
            圖6-5  分配后的內(nèi)存池狀態(tài)

            處理至⑥處之前,其狀態(tài)如圖6-6所示。


            圖6-6 處理至⑥處之前的內(nèi)存池狀態(tài)
            圖6-6  處理至⑥處之前的內(nèi)存池狀態(tài)

            這里需要注意的是,雖然pFree被"回收",但是pFree仍然指向m編號的單元,這個單元在回收過程中,其頭兩個字節(jié)被覆寫,但其他部分的內(nèi)容并沒有改變。而且從整個進程的內(nèi)存使用角度來看,這個m編號的單元的狀態(tài)仍然是"有效的"。因為這里的"回收"只是回收給了內(nèi)存池,而并沒有回收給進程堆,因此程序仍然可以通過pFree訪問此單元。但是這是一個很危險的操作,因為首先該單元在回收過程中頭兩個字節(jié)已被覆寫,并且該單元可能很快就會被內(nèi)存池重新分配。因此回收后通過pFree指針對這個單元的訪問都是錯誤的,讀操作會讀到錯誤的數(shù)據(jù),寫操作則可能會破壞程序中其他地方的數(shù)據(jù),因此需要格外小心。

            接著,需要判斷該內(nèi)存塊的內(nèi)部使用情況,及其在內(nèi)存塊鏈表中的位置。如果該內(nèi)存塊中省略號"……"所表示的其他部分中還有被分配的單元,即nFree乘以nUnitSize不等于nSize。因為此內(nèi)存塊不在鏈表頭,因此還需要將其移到鏈表頭部,如圖6-7所示。


            圖6-7 因回收引起的MemoryBlock移動
            圖6-7  因回收引起的MemoryBlock移動

            如果該內(nèi)存塊中省略號"……"表示的其他部分中全部都是自由分配單元,即nFree乘以nUnitSize等于nSize。因為此內(nèi)存塊不在鏈表頭,所以此時需要將此內(nèi)存塊整個回收給進程堆,回收后內(nèi)存池的結(jié)構(gòu)如圖6-8所示。


            圖6-8 回收后內(nèi)存池的結(jié)構(gòu)
            圖6-8  回收后內(nèi)存池的結(jié)構(gòu)

            一個內(nèi)存塊在申請后會初始化,主要是為了建立最初的自由分配單元鏈表,下面是其詳細代碼:


            MemoryBlock::MemoryBlock (USHORT nTypes, USHORT nUnitSize)
                        : nSize  (nTypes * nUnitSize),
                        nFree  (nTypes - 1),                     ④
                        nFirst (1),                              ⑤
                        pNext  (0)
                        {
                        char * pData = aData;                  ①
                        for (USHORT i = 1; i < nTypes; i++) ②
                        {
                        *reinterpret_cast<USHORT*>(pData) = i; ③
                        pData += nUnitSize;
                        }
                        }
                        

            這里可以看到,①處pData的初值是aData,即0編號單元。但是②處的循環(huán)中i卻是從1開始,然后在循環(huán)內(nèi)部的③處將pData的頭兩個字節(jié)值置為i。即0號單元的頭兩個字節(jié)值為1,1號單元的頭兩個字節(jié)值為2,一直到(nTypes-2)號單元的頭兩個字節(jié)值為(nTypes-1)。這意味著內(nèi)存塊初始時,其自由分配單元鏈表是從0號開始。依次串聯(lián),一直到倒數(shù)第2個單元指向最后一個單元。

            還需要注意的是,在其初始化列表中,nFree初始化為nTypes-1(而不是nTypes),nFirst初始化為 1(而不是0)。這是因為第1個單元,即0編號單元構(gòu)造完畢后,立刻會被分配。另外注意到最后一個單元初始并沒有設(shè)置頭兩個字節(jié)的值,因為該單元初始在本內(nèi)存塊中并沒有下一個自由分配單元。但是從上面例子中可以看到,當(dāng)最后一個單元被分配并回收后,其頭兩個字節(jié)會被設(shè)置。

            圖6-9所示為一個內(nèi)存塊初始化后的狀態(tài)。


            圖6-9 一個內(nèi)存塊初始化后的狀態(tài)
            圖6-9  一個內(nèi)存塊初始化后的狀態(tài)

            當(dāng)內(nèi)存池析構(gòu)時,需要將內(nèi)存池的所有內(nèi)存塊返回給進程堆:


            MemoryPool::~MemoryPool()
                        {
                        MemoryBlock* pMyBlock = pBlock;
                        while ( pMyBlock )
                        {
                        ……
                        }
                        }
                        

            6.2.4 使用方法

            分析內(nèi)存池的內(nèi)部原理后,本節(jié)說明如何使用它。從上面的分析可以看到,該內(nèi)存池主要有兩個對外接口函數(shù),即Alloc和 Free。Alloc返回所申請的分配單元(固定大小內(nèi)存),F(xiàn)ree則回收傳入的指針代表的分配單元的內(nèi)存給內(nèi)存池。分配的信息則通過 MemoryPool的構(gòu)造函數(shù)指定,包括分配單元大小、內(nèi)存池第1次申請的內(nèi)存塊中所含分配單元的個數(shù),以及內(nèi)存池后續(xù)申請的內(nèi)存塊所含分配單元的個數(shù)等。

            綜上所述,當(dāng)需要提高某些關(guān)鍵類對象的申請/回收效率時,可以考慮將該類所有生成對象所需的空間都從某個這樣的內(nèi)存池中開辟。在銷毀對象時,只需要返回給該內(nèi)存池。"一個類的所有對象都分配在同一個內(nèi)存池對象中"這一需求很自然的設(shè)計方法就是為這樣的類聲明一個靜態(tài)內(nèi)存池對象,同時為了讓其所有對象都從這個內(nèi)存池中開辟內(nèi)存,而不是缺省的從進程堆中獲得,需要為該類重載一個new運算符。因為相應(yīng)地,回收也是面向內(nèi)存池,而不是進程的缺省堆,還需要重載一個delete運算符。在new運算符中用內(nèi)存池的Alloc函數(shù)滿足所有該類對象的內(nèi)存請求,而銷毀某對象則可以通過在 delete運算符中調(diào)用內(nèi)存池的Free完成。

            6.2.5 性能比較

            為了測試利用內(nèi)存池后的效果,通過一個很小的測試程序可以發(fā)現(xiàn)采用內(nèi)存池機制后耗時為297 ms。而沒有采用內(nèi)存池機制則耗時625 ms,速度提高了52.48%。速度提高的原因可以歸結(jié)為幾點,其一,除了偶爾的內(nèi)存申請和銷毀會導(dǎo)致從進程堆中分配和銷毀內(nèi)存塊外,絕大多數(shù)的內(nèi)存申請和銷毀都由內(nèi)存池在已經(jīng)申請到的內(nèi)存塊中進行,而沒有直接與進程堆打交道,而直接與進程堆打交道是很耗時的操作;其二,這是單線程環(huán)境的內(nèi)存池,可以看到內(nèi)存池的Alloc和Free操作中并沒有加線程保護措施。因此如果類A用到該內(nèi)存池,則所有類A對象的創(chuàng)建和銷毀都必須發(fā)生在同一個線程中。但如果類A 用到內(nèi)存池,類B也用到內(nèi)存池,那么類A的使用線程可以不必與類B的使用線程是同一個線程。

            另外,在第1章中已經(jīng)討論過,因為內(nèi)存池技術(shù)使得同類型的對象分布在相鄰的內(nèi)存區(qū)域,而程序會經(jīng)常對同一類型的對象進行遍歷操作。因此在程序運行過程中發(fā)生的缺頁應(yīng)該會相應(yīng)少一些,但這個一般只能在真實的復(fù)雜應(yīng)用環(huán)境中進行驗證。

            6.3 本章小結(jié)

            內(nèi)存的申請和釋放對一個應(yīng)用程序的整體性能影響極大,甚至在很多時候成為某個應(yīng)用程序的瓶頸。消除內(nèi)存申請和釋放引起的瓶頸的方法往往是針對內(nèi)存使用的實際情況提供一個合適的內(nèi)存池。內(nèi)存池之所以能夠提高性能,主要是因為它能夠利用應(yīng)用程序的實際內(nèi)存使用場景中的某些"特性"。比如某些內(nèi)存申請與釋放肯定發(fā)生在一個線程中,某種類型的對象生成和銷毀與應(yīng)用程序中的其他類型對象要頻繁得多,等等。針對這些特性,可以為這些特殊的內(nèi)存使用場景提供量身定做的內(nèi)存池。這樣能夠消除系統(tǒng)提供的缺省內(nèi)存機制中,對于該實際應(yīng)用場景中的不必要的操作,從而提升應(yīng)用程序的整體性能。

            posted @ 2011-04-05 20:18 小果子 閱讀(2063) | 評論 (0)編輯 收藏

             內(nèi)存泄露相信對C++程序員來說都不陌生。解決內(nèi)存泄露的方案多種多樣,大部分方案以追蹤檢測為主,這種方法實現(xiàn)起來容易,使用方便,也比較安全。

                     首先我們要確定這個模塊的主要功能:

            1. 能追蹤內(nèi)存的分配和釋放過程。
            2. 要能顯示內(nèi)存分配的相關(guān)信息,比如內(nèi)存塊大小,代碼所在文件所在行等。
            3. 在發(fā)現(xiàn)內(nèi)存泄露時及時給出相關(guān)信息。
            4. 能正確處理一些異常情況,比如內(nèi)存不足,對象初始化失敗等等。
            5. 是線程安全的。[*這個還沒有實現(xiàn)]

                    有了一些基本功能需求,我們需要考慮每種功能怎么去實現(xiàn)。首先,我們可以通過重載的方式來追蹤new,delete.malloc和free,C++給我提供了這樣的特性。因為本文主要針對C++,所以主要講重載new,delete的方法,malloc和free的重載實現(xiàn)于此類似,最終版本的程序中也實現(xiàn)了malloc和free的重載。

            1.重載new和delete

                    首先我們要了解一下new和delete是怎么工作的。C++中的操作符最終都會被轉(zhuǎn)換成函數(shù)形式,例如"new int"會變成"opetaor new(sizeof(int))",而"new double[10]"會變成"operator new(sizeof(double)*10)",同樣“delete p”就變成了"operator delete(p)"。另外一個需要特別注意的地方是,new對于用戶定義的數(shù)據(jù)類型(即你的自定義類)會自動調(diào)用該類型的構(gòu)造函數(shù),如果構(gòu)造函數(shù)沒有拋出異常,則正確分配,否則會中斷分配操作,將異常傳遞給用戶。默認情況下,new可以對象構(gòu)造異常進行捕獲。另外一個版本的new就是不帶捕獲異常功能的的了,所以C++系統(tǒng)提供的new和delete有:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            void* operator new(size_t size)throw(std::bad_alloc);
            void* operator new[](size_t size) throw(std::bad_alloc);
            void* operator new(size_t,std::nothrow_t&)throw();
            void* operator new[](size_t,std::nothrow_t&)throw();
             
            void  operator delete(void* pointer);
            void  operator delete[](void* pointer);
            void  operator delete(void* pointer,std::nothrow_t&);
            void  operator delete[](void* pointer,std::nothrow_t&);<br>

                    其中,nothrow_t是一個空結(jié)構(gòu)體“struct nothrow_t{}",它的一個實例就是nothrow,C++用它來區(qū)分可以捕獲異常的new和不可捕獲異常的new。我們不能直接修改內(nèi)部函數(shù)的行為,但是我們可以重載它們。為了實現(xiàn)提供內(nèi)存分配信息的功能,我們給重載的函數(shù)加上幾個參數(shù)。得到以下函數(shù):

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            void* operator new(size_t size);
            void* operator new[](size_t size);
            void* operator new(size_t,std::nothrow_t&)throw();
            void* operator new[](size_t,std::nothrow_t&)throw();
            void* operator new(size_t size,const char* file,const char* func,const int line)throw(std::bad_alloc);
            void* operator new[](size_t size,const char* file,const char* func,const int line) throw(std::bad_alloc);
            void* operator delete(void* pointer);
            void* operator delete[](void* pointer);
            /*******Placement Delete********/
            void  operator delete(void* pointer,const char* file,const char* func,const int line);
            void  operator delete[](void* pointer,const char* file,const char* func,const int line);
            void  operator delete(void* pointer,std::nothrow_t&);
            void  operator delete[](void* pointer,std::nothrow_t&);
            /*******************************/

                     中間的幾個函數(shù),就是我們主要需要重載的函數(shù),模塊的大部分工作也都由著幾個函數(shù)完成。這些函數(shù)參數(shù)中,file表示分配代碼所在的文件名,func表示代碼所在的函數(shù)名,line表示代碼行號。這幾個參數(shù)信息我們可以通過編譯器預(yù)定義好的幾個宏來獲得:__FILE__,__FUNCTION__,__LINE__。也就是說可以將"new ..."替換成"new(__FILE__,__FUNCTION__,__LINE__) ...",最終成為"operator new(sizeof(...),__FILE__,__FUNCTION__,__LINE__)"的形式,也就達到了我們的目的。關(guān)于 placement delete將在下面詳細解釋。

            2.空間分配

                    接下來我們要考慮內(nèi)存分配信息的組織問題了。我們先來了解一下編譯器是怎么組織的。在大部分編譯器中,new所分配的空間都要大于實際申請的空間,大出來的部分就是編譯器定義的內(nèi)存塊的信息,包括了內(nèi)存塊的大小還有一些其他信息。如下圖所示:

                    我們把包含內(nèi)存分配信息的部分叫做cookie數(shù)據(jù)。為了方便,我們把cookie數(shù)據(jù)放在分配的內(nèi)存的起始位置,之后緊接有效數(shù)據(jù)區(qū)。我們還需要把返回給調(diào)用者的指針和new分配的數(shù)據(jù)區(qū)聯(lián)系起來,原本想用性能比較好的STL的map數(shù)據(jù)結(jié)構(gòu)來儲存這些數(shù)據(jù),但是map內(nèi)部同樣也使用new來分配內(nèi)存,所以如果直接使用map來儲存,就會陷入死循環(huán)中。所以這里我們必須自己現(xiàn)實一個數(shù)據(jù)結(jié)構(gòu)。我們可以對返回給調(diào)用者的地址進行Hash,得到hash 表中的地址,具有相同Hash值的數(shù)據(jù)我們用一個單向鏈表連接起來。最終的數(shù)據(jù)結(jié)構(gòu)如下圖所示:

            2.1.構(gòu)造函數(shù)中的異常

                    另外一個必須要注意的一點是,new操作符會先分配空間然后調(diào)用用戶自定義類型的構(gòu)造函數(shù),如果構(gòu)造函數(shù)拋出異常,需要用戶手動釋放已分配的內(nèi)存。問題在于釋放這樣的內(nèi)存不能用一般的delete操作符,可以用一個例子來說明:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            #include <stdio.h>
            #include <stdexcept>
             
            void* operator new(size_t size, int line) {
                printf("Allocate %u bytes on line %d\\n", size, line);
                return operator new(size);
            }
             
            class UserClass {
            public:
                UserClass(int n)throw(){
                    if(n<=0){
                         throw std::runtime_error("n must be positive");
                    }
                }
            };
             
            int main(){
                try{
                    UserClass* myobj=new(__LINE__) UserClass(-10);
                    delete myobj; //doesn't work if placement was not defined
                } catch(const std::runtime_error& e) {
                    fprintf(stderr,"Exception: %s\n",e.what());
                }
                return 0;
            }<br>

                    這里,雖然在new過后試圖使用delete釋放已經(jīng)分配的內(nèi)存,但是實際上不會釋放。也許你的編譯器會給出這樣一條消息:

                    “no matching operator delete found”

                    為了正確處理這種情況,并給用戶提供相關(guān)的信息,我們需要定義placement delete操作符。placement delete是C++98標準中才有的一個特性,所以對于某些老的編譯器(大致可以認為是指那些98年以前編寫的編譯器)不支持這個特性。這需要在模塊中添加宏定義讓用戶可以關(guān)閉placement delete的定義,以便模塊能在較老的編譯器上編譯。以下就是需要定義的placement delete操作符:

            1
            2
            3
            4
            void  operator delete(void* pointer,const char* file,const char* func,const int line);
            void  operator delete[](void* pointer,const char* file,const char* func,const int line);
            void  operator delete(void* pointer,std::nothrow_t&);
            void  operator delete[](void* pointer,std::nothrow_t&);<br>

            3.檢查內(nèi)存泄露

                    有了上面的實現(xiàn),我們可以方便的手動檢測內(nèi)存泄露。通過一個函數(shù)來實現(xiàn),它會檢索整個hash表,如果表不為空則存在內(nèi)存泄露。

                    為了達到在最后程序退出時檢查內(nèi)存泄露的目的,我們需要在所有對象調(diào)用析構(gòu)函數(shù)后進行內(nèi)存泄露檢測,這是因為某些用戶類型在構(gòu)造函數(shù)里調(diào)用new而在析構(gòu)函數(shù)里調(diào)用delete。這樣做能大大的減小誤報的概率。而且因為對象的析構(gòu)函數(shù)的調(diào)用往往在主函數(shù)main()執(zhí)行結(jié)束之后進行,所以我們也不能直接在主函數(shù)里進行內(nèi)存泄露檢測。這里我們利用一個全局對象來實現(xiàn)這種檢測。首先我們定義一個類:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            class MemCheck{
                public:
                    MemCheck(){
                        memset(pTable,0,sizeof(mc_block_node_t*) * MC_HASHTABLESIZE);
                    }
                    ~MemCheck(){
                        if(mc_checkmem()){
                            abort();
                        }
                    }
            };

                    這里的構(gòu)造函數(shù)初始化Hash表。析構(gòu)函數(shù)檢測內(nèi)存泄露。然后定義一個MemCheck的全局靜態(tài)對象,這樣當(dāng)程序運行之前會初始化hash表,程序退出時檢測內(nèi)存泄露。可是問題又來了,如果一個程序中有多個全局靜態(tài)對象會怎樣?不幸的是,對于全局靜態(tài)對象的構(gòu)造順序和析構(gòu)順序是C++標準中的一個未定義問題,也就是說,這個順序取決于編譯器的具體實現(xiàn)??紤],絕大多數(shù)平臺使用VC和GCC編譯器,我們可以針對這兩種編譯器來控制全局對象的構(gòu)造和解析順序。

            1
            2
            3
            4
            5
            6
            7
            8
            9
            #ifdef _MSC_VER
            #pragma init_seg(lib)
            #endif
             
            static MemCheck mc_autocheck_object
            #ifdef __GNUC__
            __attribute__((init_priority (101)))
            #endif
            ;

                    這里的宏定義部分都是編譯器的選項。

            posted @ 2011-04-05 20:11 小果子 閱讀(1643) | 評論 (1)編輯 收藏
            提交服務(wù)器處理業(yè)務(wù)后結(jié)果返回頁面的處理,Struts2提供了對不同種類返回結(jié)果的支持,常見的有JSP,F(xiàn)reeMarker,Velocity等。

            struts.xml配置文件中result的語法:<result name="" type="">xxxxx</result>

            Struts2支持的不同類型的返回結(jié)果為:

            Chain Result-->type="chain"
            用來處理Action鏈

            Dispatcher Result -->type="dispatcher"
            用來轉(zhuǎn)向頁面,通常處理JSP

            FreeMarker Result -->type="freemarker"
            處理FreeMarker模板

            HttpHeader Result -->type="httpheader"
            用來控制特殊的Http行為

            Redirect Result -->type="redirect"
            重定向到一個URL

            Redirect Action Result -->type="redirectAction"
            重定向到一個Action

            Stream Result -->type="stream"
            向瀏覽器發(fā)送InputSream對象,通常用來處理文件下載

            Velocity Result -->type="velocity"
            處理Velocity模板

            XLST Result -->type="xslt"
            處理XML/XLST模板

            PlainText Result -->type="plainText"
            顯示原始文件內(nèi)容,例如文件源代碼


            另外第三方的result類型還包括JasperReports Plugin,專門用來處理JasperReport類型的報表輸出。

            在struts-default.xml文件中已經(jīng)有了對于所有類型Result的定義:

            Java 代碼

            1. <result-types>  
            2.  
            3.     <result-type name="chain"  
            4.  
            5.              class="com.opensymphony.xwork2.ActionChainResult"/>  
            6.  
            7.     <result-type name="dispatcher"  
            8.  
            9.              class="org.apache.struts2.dispatcher.ServletDispatcherResult"  
            10.  
            11.              default="true"/>  
            12.  
            13.     <result-type name="freemarker"  
            14.  
            15.              class="org.apache.struts2.views.freemarker.FreemarkerResult"/>  
            16.  
            17.     <result-type name="httpheader"  
            18.  
            19.              class="org.apache.struts2.dispatcher.HttpHeaderResult"/>  
            20.  
            21.     <result-type name="redirect"  
            22.  
            23.              class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  
            24.  
            25.     <result-type name="redirectAction"  
            26.  
            27.              class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>  
            28.  
            29.     <result-type name="stream"  
            30.  
            31.              class="org.apache.struts2.dispatcher.StreamResult"/>  
            32.  
            33.     <result-type name="velocity"  
            34.  
            35.              class="org.apache.struts2.dispatcher.VelocityResult"/>  
            36.  
            37.     <result-type name="xslt"  
            38.  
            39.              class="org.apache.struts2.views.xslt.XSLTResult"/>  
            40.  
            41.     <result-type name="plainText"  
            42.  
            43.              class="org.apache.struts2.dispatcher.PlainTextResult" />  
            44.  
            45.     <!-- Deprecated name form scheduled for removal in Struts 2.1.0.  
            46.  
            47.          The camelCase versions are preferred. See ww-1707 -->  
            48.  
            49.     <result-type name="redirect-action"  
            50.  
            51.              class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>  
            52.  
            53.     <result-type name="plaintext"  
            54.  
            55.              class="org.apache.struts2.dispatcher.PlainTextResult" />  
            56.  
            57. </result-types>  

            <result-types>

            <result-type name="chain"

            class="com.opensymphony.xwork2.ActionChainResult"/>

            <result-type name="dispatcher"

            class="org.apache.struts2.dispatcher.ServletDispatcherResult"

            default="true"/>

            <result-type name="freemarker"

            class="org.apache.struts2.views.freemarker.FreemarkerResult"/>

            <result-type name="httpheader"

            class="org.apache.struts2.dispatcher.HttpHeaderResult"/>

            <result-type name="redirect"

            class="org.apache.struts2.dispatcher.ServletRedirectResult"/>

            <result-type name="redirectAction"

            class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>

            <result-type name="stream"

            class="org.apache.struts2.dispatcher.StreamResult"/>

            <result-type name="velocity"

            class="org.apache.struts2.dispatcher.VelocityResult"/>

            <result-type name="xslt"

            class="org.apache.struts2.views.xslt.XSLTResult"/>

            <result-type name="plainText"

            class="org.apache.struts2.dispatcher.PlainTextResult" />

            <!-- Deprecated name form scheduled for removal in Struts 2.1.0.

            The camelCase versions are preferred. See ww-1707 -->

            <result-type name="redirect-action"

            class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>

            <result-type name="plaintext"

            class="org.apache.struts2.dispatcher.PlainTextResult" />

            </result-types>



            從上述代碼中可以看出在不指定Result類型的時候默認使用dispatcher類型。


            定義一個Result值,

            Java 代碼

            1. <result name="success" type="dispatcher">  
            2.  
            3.     <param name="location">/myjsp.jsp</param>  
            4.  
            5. </result>  

            <result name="success" type="dispatcher">

            <param name="location">/myjsp.jsp</param>

            </result>


            由于type默認值是dispatcher,所以這里不需要定義,另外name的默認值為success所以這里也不需要定義。
            上述代碼可以簡寫為:

            Java 代碼

            1. <result>  
            2.  
            3.     <param name="location">/myjsp.jsp</param>  
            4.  
            5. </result>  

            <result>

            <param name="location">/myjsp.jsp</param>

            </result>




            另外location參數(shù)也可以直接卸載result標簽內(nèi)部(也就是無需再result里面使用),所以上述代碼的最簡單的寫法為:

            Java 代碼

            1. <result>/myjsp.jsp</result>  

            <result>/myjsp.jsp</result>



            我們也可以定義多個不同的result

            Java 代碼

            1. <action name="Hello">  
            2.  
            3. <result>/hello/hello.jsp</result>  
            4.  
            5. <result name="error">/hello/error.jsp</result>  
            6.  
            7. <result name="input">/hello/input.jsp</result>  
            8.  
            9. </action>  

            <action name="Hello">

            <result>/hello/hello.jsp</result>

            <result name="error">/hello/error.jsp</result>

            <result name="input">/hello/input.jsp</result>

            </action>



            上 述代碼的含義為,名字為Hello的Action有三個返回結(jié)果,并且都是 dispatcher類型(默認類型),這三個返回值的名字分別為success(默認值),error,input(當(dāng)輸入不通過時,action 方法返回input),對應(yīng)的頁面的路徑分別為 /hello/result.jsp,/hello/error.jsp,/hello/input.jsp。

            有些時候我們需要一個定義在全局的result,這個時候我們可以在package內(nèi)部定義全局的result,例如:

            Java 代碼

            1. <global-results>  
            2.  
            3. <result name="error">/error.jsp</result>  
            4.  
            5. <result name="invalid.token">/error.jsp</result>  
            6.  
            7. <result name="login" type="redirect-action">login!input</result>  
            8.  
            9. </global-results>  

            <global-results>

            <result name="error">/error.jsp</result>

            <result name="invalid.token">/error.jsp</result>

            <result name="login" type="redirect-action">login!input</result>

            </global-results>



            動態(tài)返回結(jié)果

            有些時候,只有當(dāng)Action執(zhí)行完璧的時候我們才知道要返回哪個結(jié)果,這個時候我們可以在Action內(nèi)部定義一個屬性,這個屬性用來存儲 Action執(zhí)行完璧之后的Result值,例如:

            Java 代碼

            1. private String nextAction;  
            2.  
            3. public String getNextAction() {  
            4.  
            5.     return nextAction;  
            6.  
            7. }  

            private String nextAction;

            public String getNextAction() {

            return nextAction;

            }



            在strutx.xml配置文件中,我們可以使用${nextAction}來引用到Action中的屬性,通過${nextAction}表示的內(nèi)容來動態(tài)的返回結(jié)果,例如:

            Java 代碼

            1. <action name="fragment" class="FragmentAction">  
            2.  
            3. <result name="next" type="redirect-action">${nextAction}</result>  
            4.  
            5. </action>  

            <action name="fragment" class="FragmentAction">

            <result name="next" type="redirect-action">${nextAction}</result>

            </action>



            上述Action的execute方法返回next的時候,還需要根據(jù)nextAction的屬性來判斷具體定位到哪個Action。


            在struts.xml配置文件中,我們可以使用method=""來設(shè)置調(diào)用類的哪個方法,這樣就可以在一個JAVA類中使用不同的方法來實現(xiàn)不同的功能,就無需每個功能寫一類了,例如:
            Java 代碼

            1. <action name="fragment" class="cn.com.web.FragmentAction" method="add">  
            2.       <result>/success.jsp</result>  
            3. </action> 
            posted @ 2011-04-05 18:17 小果子 閱讀(530) | 評論 (0)編輯 收藏

            在struts的配置文件中的形式為:

            <constant name="struts.i18n.encoding" value="UTF-8" />

            struts.action.extension
            The URL extension to use to determine if the request is meant for a Struts action
            用URL擴展名來確定是否這個請求是被用作Struts action,其實也就是設(shè)置 action的后綴,例如login.do的'do'字。

            struts.configuration
            The org.apache.struts2.config.Configuration implementation class
            org.apache.struts2.config.Configuration接口名

            struts.configuration.files
            A list of configuration files automatically loaded by Struts
            struts自動加載的一個配置文件列表

            struts.configuration.xml.reload
            Whether to reload the XML configuration or not
            是否加載xml配置(true,false)

            struts.continuations.package
            The package containing actions that use Rife continuations
            含有actions的完整連續(xù)的package名稱

            struts.custom.i18n.resources
            Location of additional localization properties files to load
            加載附加的國際化屬性文件(不包含.properties后綴)

            struts.custom.properties
            Location of additional configuration properties files to load
            加載附加的配置文件的位置


            struts.devMode
            Whether Struts is in development mode or not
            是否為struts開發(fā)模式

            struts.dispatcher.parametersWorkaround
            Whether to use a Servlet request parameter workaround necessary for some versions of WebLogic
            (某些版本的weblogic專用)是否使用一個servlet請求參數(shù)工作區(qū)(PARAMETERSWORKAROUND)

            struts.enable.DynamicMethodInvocation
            Allows one to disable dynamic method invocation from the URL
            允許動態(tài)方法調(diào)用

            struts.freemarker.manager.classname
            The org.apache.struts2.views.freemarker.FreemarkerManager implementation class
            org.apache.struts2.views.freemarker.FreemarkerManager接口名

            struts.i18n.encoding
            The encoding to use for localization messages
            國際化信息內(nèi)碼

            struts.i18n.reload
            Whether the localization messages should automatically be reloaded
            是否國際化信息自動加載

            struts.locale
            The default locale for the Struts application
            默認的國際化地區(qū)信息

            struts.mapper.class
            The org.apache.struts2.dispatcher.mapper.ActionMapper implementation class
            org.apache.struts2.dispatcher.mapper.ActionMapper接口

            struts.multipart.maxSize
            The maximize size of a multipart request (file upload)
            multipart請求信息的最大尺寸(文件上傳用)

            struts.multipart.parser
            The org.apache.struts2.dispatcher.multipart.MultiPartRequest parser implementation for a multipart request (file upload)
            專為multipart請求信息使用的 org.apache.struts2.dispatcher.multipart.MultiPartRequest解析器接口(文件上傳用)


            struts.multipart.saveDir
            The directory to use for storing uploaded files
            設(shè)置存儲上傳文件的目錄夾

            struts.objectFactory
            The com.opensymphony.xwork2.ObjectFactory implementation class
            com.opensymphony.xwork2.ObjectFactory接口(spring)

            struts.objectFactory.spring.autoWire
            Whether Spring should autoWire or not
            是否自動綁定Spring

            struts.objectFactory.spring.useClassCache
            Whether Spring should use its class cache or not
            是否spring應(yīng)該使用自身的cache

            struts.objectTypeDeterminer
            The com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation class
            com.opensymphony.xwork2.util.ObjectTypeDeterminer接口

            struts.serve.static.browserCache
            If static content served by the Struts filter should set browser caching header properties or not
            是否struts過濾器中提供的靜態(tài)內(nèi)容應(yīng)該被瀏覽器緩存在頭部屬性中

            struts.serve.static
            Whether the Struts filter should serve static content or not
            是否struts過濾器應(yīng)該提供靜態(tài)內(nèi)容

            struts.tag.altSyntax
            Whether to use the alterative syntax for the tags or not
            是否可以用替代的語法替代tags

            struts.ui.templateDir
            The directory containing UI templates
            UI templates的目錄夾

            struts.ui.theme
            The default UI template theme
            默認的UI template主題

            struts.url.http.port
            The HTTP port used by Struts URLs
            設(shè)置http端口

            struts.url.https.port
            The HTTPS port used by Struts URLs
            設(shè)置https端口

            struts.url.includeParams
            The default includeParams method to generate Struts URLs
            在url中產(chǎn)生 默認的includeParams


            struts.velocity.configfile
            The Velocity configuration file path
            velocity配置文件路徑

            struts.velocity.contexts
            List of Velocity context names
            velocity的context列表


            struts.velocity.manager.classname
            org.apache.struts2.views.velocity.VelocityManager implementation class
            org.apache.struts2.views.velocity.VelocityManager接口名

            struts.velocity.toolboxlocation
            The location of the Velocity toolbox
            velocity工具盒的位置
            struts.xslt.nocache
            Whether or not XSLT templates should not be cached
            是否XSLT模版應(yīng)該被緩存

            【原創(chuàng)】struts2的struts.properties配置文件詳解

            struts.action.extension
            The URL extension to use to determine if the request is meant for a Struts action
            用URL擴展名來確定是否這個請求是被用作Struts action,其實也就是設(shè)置 action的后綴,例如login.do的'do'字。

            struts.configuration
            The org.apache.struts2.config.Configuration implementation class
            org.apache.struts2.config.Configuration接口名

            struts.configuration.files
            A list of configuration files automatically loaded by Struts
            struts自動加載的一個配置文件列表

            struts.configuration.xml.reload
            Whether to reload the XML configuration or not
            是否加載xml配置(true,false)

            struts.continuations.package
            The package containing actions that use Rife continuations
            含有actions的完整連續(xù)的package名稱

            struts.custom.i18n.resources
            Location of additional localization properties files to load
            加載附加的國際化屬性文件(不包含.properties后綴)

            struts.custom.properties
            Location of additional configuration properties files to load
            加載附加的配置文件的位置


            struts.devMode
            Whether Struts is in development mode or not
            是否為struts開發(fā)模式

            struts.dispatcher.parametersWorkaround
            Whether to use a Servlet request parameter workaround necessary for some versions of WebLogic
            (某些版本的weblogic專用)是否使用一個servlet請求參數(shù)工作區(qū)(PARAMETERSWORKAROUND)

            struts.enable.DynamicMethodInvocation
            Allows one to disable dynamic method invocation from the URL
            允許動態(tài)方法調(diào)用

            struts.freemarker.manager.classname
            The org.apache.struts2.views.freemarker.FreemarkerManager implementation class
            org.apache.struts2.views.freemarker.FreemarkerManager接口名

            struts.i18n.encoding
            The encoding to use for localization messages
            國際化信息內(nèi)碼

            struts.i18n.reload
            Whether the localization messages should automatically be reloaded
            是否國際化信息自動加載

            struts.locale
            The default locale for the Struts application
            默認的國際化地區(qū)信息

            struts.mapper.class
            The org.apache.struts2.dispatcher.mapper.ActionMapper implementation class
            org.apache.struts2.dispatcher.mapper.ActionMapper接口

            struts.multipart.maxSize
            The maximize size of a multipart request (file upload)
            multipart請求信息的最大尺寸(文件上傳用)

            struts.multipart.parser
            The org.apache.struts2.dispatcher.multipart.MultiPartRequest parser implementation for a multipart request (file upload)
            專為multipart請求信息使用的 org.apache.struts2.dispatcher.multipart.MultiPartRequest解析器接口(文件上傳用)


            struts.multipart.saveDir
            The directory to use for storing uploaded files
            設(shè)置存儲上傳文件的目錄夾

            struts.objectFactory
            The com.opensymphony.xwork2.ObjectFactory implementation class
            com.opensymphony.xwork2.ObjectFactory接口(spring)

            struts.objectFactory.spring.autoWire
            Whether Spring should autoWire or not
            是否自動綁定Spring

            struts.objectFactory.spring.useClassCache
            Whether Spring should use its class cache or not
            是否spring應(yīng)該使用自身的cache

            struts.objectTypeDeterminer
            The com.opensymphony.xwork2.util.ObjectTypeDeterminer implementation class
            com.opensymphony.xwork2.util.ObjectTypeDeterminer接口

            struts.serve.static.browserCache
            If static content served by the Struts filter should set browser caching header properties or not
            是否struts過濾器中提供的靜態(tài)內(nèi)容應(yīng)該被瀏覽器緩存在頭部屬性中

            struts.serve.static
            Whether the Struts filter should serve static content or not
            是否struts過濾器應(yīng)該提供靜態(tài)內(nèi)容

            struts.tag.altSyntax
            Whether to use the alterative syntax for the tags or not
            是否可以用替代的語法替代tags

            struts.ui.templateDir
            The directory containing UI templates
            UI templates的目錄夾

            struts.ui.theme
            The default UI template theme
            默認的UI template主題

            struts.url.http.port
            The HTTP port used by Struts URLs
            設(shè)置http端口

            struts.url.https.port
            The HTTPS port used by Struts URLs
            設(shè)置https端口

            struts.url.includeParams
            The default includeParams method to generate Struts URLs
            在url中產(chǎn)生 默認的includeParams


            struts.velocity.configfile
            The Velocity configuration file path
            velocity配置文件路徑

            struts.velocity.contexts
            List of Velocity context names
            velocity的context列表


            struts.velocity.manager.classname
            org.apache.struts2.views.velocity.VelocityManager implementation class
            org.apache.struts2.views.velocity.VelocityManager接口名

            struts.velocity.toolboxlocation
            The location of the Velocity toolbox
            velocity工具盒的位置
            struts.xslt.nocache
            Whether or not XSLT templates should not be cached
            是否XSLT模版應(yīng)該被緩存

            struts2加載常量的順序
            struts-default.xml
            struts-plugin.xml
            struts.xml
            struts.properties
            web.xml
            后面的會覆蓋掉前面的常量,最好在struts.xml中定義

             

            怎么由.action改為.do
            <constant name="struts.action.extension" value="do"/>
            do或action
            <constant name="struts.action.extension" value="do,action"/>


            truts2用來指定默認編碼的
            <constant name="struts.i18n.encoding" value="UTF-8"/>

             

            改變常量后不許重啟服務(wù)器
            <constant name="struts.configuration.xml.reload" value="true"/>
            系統(tǒng)默認為false 


            便于排錯,打印出更詳細的錯誤信息
            <constant name="struts.devMode" value="true">

             

            設(shè)置瀏覽器是否緩存靜態(tài)內(nèi)容,默認為TRUE  開發(fā)階段最好關(guān)閉
            <constant name="struts.server.static.browserCache" valur="false"/>

             

            默認的視圖主題
            <constant name="struts.ui.theme" value="simple"/>

             

            與spring集成時,指定spring負責(zé)action對象的創(chuàng)建
            <struts name="struts.objectFactory" value="spring"/>

             

            上傳文件大小限制
            <struts name="struts.multipart.maxSize" value="10241024"/>

            posted @ 2011-04-05 17:58 小果子 閱讀(481) | 評論 (0)編輯 收藏
            僅列出標題
            共58頁: First 24 25 26 27 28 29 30 31 32 Last 
            久久久精品波多野结衣| 中文成人无码精品久久久不卡| 久久天天躁狠狠躁夜夜avapp| 久久亚洲av无码精品浪潮| 综合久久给合久久狠狠狠97色| 久久天天躁狠狠躁夜夜不卡| 久久午夜羞羞影院免费观看| 国产精品久久午夜夜伦鲁鲁| 国产精品无码久久综合网| 伊人久久无码精品中文字幕| 久久大香香蕉国产| 热RE99久久精品国产66热| 亚洲狠狠婷婷综合久久久久 | 精品久久久久香蕉网| 久久久久人妻一区精品 | 成人国内精品久久久久影院| 久久亚洲精品无码观看不卡| 欧美午夜精品久久久久免费视| 久久国产免费| 狠狠色丁香婷综合久久| 伊人久久大香线蕉亚洲| 久久久久久A亚洲欧洲AV冫| 国产精品久久永久免费| 天天躁日日躁狠狠久久 | 精品久久久久久无码人妻蜜桃| 亚洲AV无码成人网站久久精品大| 久久久久国产一区二区三区| 久久精品国产91久久综合麻豆自制 | 日韩精品国产自在久久现线拍| 伊人久久综合精品无码AV专区| 亚洲国产精品无码久久久久久曰| 国内精品久久久久久久久| 久久久精品午夜免费不卡| 99久久国产综合精品麻豆| 色欲久久久天天天综合网| 久久综合鬼色88久久精品综合自在自线噜噜| 伊人久久大香线蕉精品| 国产99久久精品一区二区| 成人免费网站久久久| 国内精品伊人久久久久| 久久99精品久久久久久|