1、請高手指教: linux 內存優化怎麼做? 就是在申請內存時怎麼做到較少內存碎片??
你的情況都沒說明,怎麼能夠讓人知道症狀,
我不是什麼大蝦,但是我知道為什麼
linux支持數十種文件系統。ext3/ext2/xfs等,包括windows的NTFS和FAT等等,
而windows主要使用NTFS文件系統,兼容FAT文件系統,
你安裝的時候肯定是選用了NTFS安裝,導致出錯,這個原因應該是硬碟的文件分區表出錯,如果你用FAT文件系統安裝,應該不會出錯,至於原理,希望你把具體症狀說出。
2、內存碎片的減少內存碎片
內存碎片是因為在分配一個內存塊後,使之空閑,但不將空閑內存歸還給最大內存塊而產生的。最後這一步很關鍵。如果內存分配程序是有效的,就不能阻止系統分配內存塊並使之空閑。即使一個內存分配程序不能保證返回的內存能與最大內存塊相連接(這種方法可以徹底避免內存碎片問題),但你可以設法控制並限制內存碎片。所有這些作法涉及到內存塊的分割。每當系統減少被分割內存塊的數量,確保被分割內存塊盡可能大時,你就會有所改進。
這樣做的目的是盡可能多次反復使用內存塊,而不要每次都對內存塊進行分割,以正好符合請求的存儲量。分割內存塊會產生大量的小內存碎片,猶如一堆散沙。以後很難把這些散沙與其餘內存結合起來。比較好的辦法是讓每個內存塊中都留有一些未用的位元組。留有多少位元組應看系統要在多大程度上避免內存碎片。對小型系統來說,增加幾個位元組的內部碎片是朝正確方向邁出的一步。當系統請求1位元組內存時,你分配的存儲量取決於系統的工作狀態。
如果系統分配的內存存儲量的主要部分是 1 ~ 16 位元組,則為小內存也分配 16 位元組是明智的。只要限制可以分配的最大內存塊,你就能夠獲得較大的節約效果。但是,這種方法的缺點是,系統會不斷地嘗試分配大於極限的內存塊,這使系統可能會停止工作。減少最大和最小內存塊存儲量之間內存存儲量的數量也是有用的。採用按對數增大的內存塊存儲量可以避免大量的碎片。例如,每個存儲量可能都比前一個存儲量大 20%。在嵌入式系統中採用「一種存儲量符合所有需要」對於嵌入式系統中的內存分配程序來說可能是不切實際的。這種方法從內部碎片來看是代價極高的,但系統可以徹底避免外部碎片,達到支持的最大存儲量。
將相鄰空閑內存塊連接起來是一種可以顯著減少內存碎片的技術。如果沒有這一方法,某些分配演算法(如最先適合演算法)將根本無法工作。然而,效果是有限的,將鄰近內存塊連接起來只能緩解由於分配演算法引起的問題,而無法解決根本問題。而且,當內存塊存儲量有限時,相鄰內存塊連接可能很難實現。
有些內存分配器很先進,可以在運行時收集有關某個系統的分配習慣的統計數據,然後,按存儲量將所有的內存分配進行分類,例如分為小、中和大三類。系統將每次分配指向被管理內存的一個區域,因為該區域包括這樣的內存塊存儲量。較小存儲量是根據較大存儲量分配的。這種方案是最先適合演算法和一組有限的固定存儲量演算法的一種有趣的混合,但不是實時的。
有效地利用暫時的局限性通常是很困難的,但值得一提的是,在內存中暫時擴展共處一地的分配程序更容易產生內存碎片。盡管其它技術可以減輕這一問題,但限制不同存儲量內存塊的數目仍是減少內存碎片的主要方法。
現代軟體環境業已實現各種避免內存碎片的工具。例如,專為分布式高可用性容錯系統開發的 OSE 實時操作系統可提供三種運行時內存分配程序:內核 alloc(),它根據系統或內存塊池來分配;堆 malloc(),根據程序堆來分配; OSE 內存管理程序 alloc_region,它根據內存管理程序內存來分配。
從 許多方面來看,Alloc就是終極內存分配程序。它產生的內存碎片很少,速度很快,並有判定功能。你可以調整甚至去掉內存碎片。只是在分配一個存儲量後,使之空閑,但不再分配時,才會產生外部碎片。內部碎片會不斷產生,但對某個給定的系統和八種存儲量來說是恆定不變的。
Alloc 是一種有八個自由表的固定存儲量內存分配程序的實現方法。系統程序員可以對每一種存儲量進行配置,並可決定採用更少的存儲量來進一步減少碎片。除開始時以外,分配內存塊和使內存塊空閑都是恆定時間操作。首先,系統必須對請求的存儲量四捨五入到下一個可用存儲量。就八種存儲量而言,這一目標可用三個 如果 語句來實現。其次,系統總是在八個自由表的表頭插入或刪除內存塊。開始時,分配未使用的內存要多花幾個周期的時間,但速度仍然極快,而且所花時間恆定不變。
堆 malloc() 的內存開銷(8 ~ 16 位元組/分配)比 alloc小,所以你可以停用內存的專用權。malloc() 分配程序平均來講是相當快的。它的內部碎片比alloc()少,但外部碎片則比alloc()多。它有一個最大分配存儲量,但對大多數系統來說,這一極限值足夠大。可選的共享所有權與低開銷使 malloc() 適用於有許多小型對象和共享對象的 C++ 應用程序。堆是一種具有內部堆數據結構的夥伴系統的實現方法。在 OSE 中,有 28 個不同的存儲量可供使用,每種存儲量都是前兩種存儲量之和,於是形成一個斐波那契(Fibonacci)序列。實際內存塊存儲量為序列數乘以 16 位元組,其中包括分配程序開銷或者 8 位元組/分配(在文件和行信息啟用的情況下為 16 位元組)。
當你很少需要大塊內存時,則OSE內存管理程序最適用。典型的系統要把存儲空間分配給整個系統、堆或庫。在有 MMU 的系統中,有些實現方法使用 MMU 的轉換功能來顯著降低甚至消除內存碎片。在其他情況下,OSE 內存管理程序會產生非常多的碎片。它沒有最大分配存儲量,而且是一種最先適合內存分配程序的實現方法。內存分配被四捨五入到頁面的偶數——典型值是 4 k 位元組。

3、程序中內存碎片到底是怎麼產生的?
大多是內存沒有釋放,指針沒刪除。要減少就要用完後釋放內存、刪除指針等
4、如何解決內存碎片
下了清理軟體 就搞定了
5、如何減少頻繁分配內存(malloc或者new)造成的內存碎片?
一般的做法是把malloc和free包裝以後使用,再加入適當的調試代碼。可以參考:編寫無錯的C程序的秘訣的電子版。比如:free(p)包裝一個函數:
void Free(void *p) {
if (p != NULL) {
free(p);
p = NULL;
}
}
至於malloc,一般的做法是分配size個位元組的內存時,多分配一個調試用的結構體,然後把這些結構體做成鏈表,可以通過查看這個鏈表找到野指針。
6、怎麼清理內存碎片?
內存碎片,其實和磁碟碎片不是一個級別的,一般不需要清理。
1、XP的內存是windows的,內存管理比較差,有一些程序退出後沒有釋放內存,有人稱為內存碎片。這個可以藉助第三方工具,比如smartram、內存救星之類的軟體來清理釋放。
2、win7以後,是使用了nt內核,內存管理十分優秀了,應用程序的內存釋放機制是比較完善的,不需要管理內存碎片。這一點,從上述軟體基本上沒有win7版就可以看出來。因為沒必要。
3、重啟是最好的整理內存碎片的辦法。
7、如何減少頻繁分配內存造成的內存碎片
windows自帶了一個磁碟碎片整理工具
打開路徑:開始->所有程序->附件->系統工具->磁碟碎片整理程序
先分析磁碟碎片再進行磁碟碎片整理。
8、[編程知識]如何分配內存 內存碎片處理技術
內存碎片是一個很棘手的問題。如何分配內存決定著內存碎片是否會、何時會、如何會成為一個問題。 即使在系統中事實上仍然有許多空閑內存時,內存碎片還會最終導致出現內存用完的情況。一個不斷產生內存碎片的系統,不管產生的內存碎片多麼小,只要時間足夠長,就會將內存用完。這種情況在許多嵌入式系統中,特別是在高可用性系統中是不可接受的。有些軟體環境,如 OSE 實時操作系統已經備有避免內存碎片的良好工具,但個別程序員做出的選擇仍然會對最終結果形成影響。「碎片的內存」描述一個系統中所有不可用的空閑內存。這些資源之所以仍然未被使用,是因為負責分配內存的分配器使這些內存無法使用。這一問題通常都會發生,原因在於空閑內存以小而不連續方式出現在不同的位置。由於分配方法決定內存碎片是否是一個問題,因此內存分配器在保證空閑資源可用性方面扮演著重要的角色。 編譯時間與運行時間在許多情況下都會出現內存分配問題。程序員可以通過編譯程序和鏈接程序,為結構、並集、數組和標量(用作局部變數、靜態變數或全局變數)方面的數據分配內存,程序員還可以在運行時間使用諸如 malloc()調用命令動態地分配內存。當用編譯程序和鏈接程序完成內存分配功能時,就不會出現內存碎片,因為編譯程序了解數據壽命。掌握可供使用的數據壽命,好處在於可以使數據以後進先出的方式疊加起來。這樣就可以使內存分配程序工作效率更高,而不會出現內存碎片。一般來說,運行時間內的內存分配是不可疊加的。內存分配在時間上是獨立的,從而使得碎片問題難以解決。圖1,內存碎片的幾種形式。 內存分配程序浪費內存的基本方式有三種:即額外開銷、內部碎片以及外部碎片(圖 1)。內存分配程序需要存儲一些描述其分配狀態的數據。這些存儲的信息包括任何一個空閑內存塊的位置、大小和所有權,以及其它內部狀態詳情。一般來說,一個運行時間分配程序存放這些額外信息最好的地方是它管理的內存。內存分配程序需要遵循一些基本的內存分配規則。例如,所有的內存分配必須起始於可被 4、8 或 16 整除(視處理器體系結構而定)的地址。內存分配程序把僅僅預定大小的內存塊分配給客戶,可能還有其它原因。當某個客戶請求一個 43 位元組的內存塊時,它可能會獲得 44位元組、48位元組 甚至更多的位元組。由所需大小四捨五入而產生的多餘空間就叫內部碎片。外部碎片的產生是當已分配內存塊之間出現未被使用的差額時,就會產生外部碎片。例如,一個應用程序分配三個連續的內存塊,然後使中間的一個內存塊空閑。內存分配程序可以重新使用中間內存塊供將來進行分配,但不太可能分配的塊正好與全部空閑內存一樣大。倘若在運行期間,內存分配程序不改變其實現法與四捨五入策略,則額外開銷和內部碎片在整個系統壽命期間保持不變。雖然額外開銷和內部碎片會浪費內存,因此是不可取的,但外部碎片才是嵌入系統開發人員真正的敵人,造成系統失效的正是分配問題。定義內存碎片的方法有幾種,其中最常用的是:這一方法適用於外部碎片,但可以修改這一公式使之包括內部碎片,辦法是把內部碎片加入到分母中。內存碎片是一個介於 0 和 1 之間的分數。一個碎片為 1(100%)的系統就是把內存全用完了。如果所有空閑內存都在一個內存塊(最大內存塊)中,碎片為 0%。當所有空閑內存的四分之一在最大內存塊中時,碎片為 75%。例子如下:一個系統有 5M 位元組的空閑內存,當它可用來分配的最大內存塊為 50 k 位元組時,其內存碎片為99%。這個 99%內存碎片實例來自開發嵌入式軟實時系統期間出現的一種真實情況。當這種碎片程度發生一秒後,系統就崩潰了。該系統在碎片率達到 99% 之前,已經進行了約兩周的連續現場測試。這種情況是如何發生的?為什麼會發現得如此晚?當然,系統都經過測試,但測試很少超過兩個小時。交付前的最後壓力測試持續了一個周末。在這樣短的測試周期內未必會產生內存碎片的後果,所以就發生了內存碎片需要多長時間才會達到臨界值,這一問題很難回答。對某些應用來說,在某些情況下,系統會在用完內存前達到一種穩定狀態。而對於另一些應用來說,系統則不會及時達到穩定狀態(圖 2)。只要消除不確定性因素和風險因素,不產生碎片的內存分配程序(圖 3)就能快速達到一種穩定狀態,從而有助於開發人員夜晚安穩睡覺。在開發數月甚至數年不再重新啟動的長期運行系統時,快速收斂到穩定狀態是一個重要因素。在比系統連續運行周期短的時間內,對系統進行適當的測試,這是必不可少的。圖2,這一案例研究把最先適合內存分配程序用於一個嵌入系統項目。系統在現場測試中連續運行了兩周,然後碎片率達到 99%。圖3,一個不產生碎片的內存分配程序一旦試驗應用程序的全部,它就能達到穩定狀態。 很難確定哪種內存分配演算法更勝一籌,因為每種演算法在不同的應用中各有所長(表 1)。最先適合內存分配演算法是最常用的一種。它使用了四個指針:MSTART 指向被管理內存的始端;MEND 指向被管理內存的末尾;MBREAK 指向 MSTART 和 MEND 之間已用內存的末端; PFREE 則指向第一個空閑內存塊(如果有的話)。在系統開始運行時,PFREE 為 NULL,MBREAK 指向 MSTART。當一個分配請求來到時,分配程序首先檢查 PFREE有無空閑內存塊。由於 PFREE 為 NULL,一個具有所請求存儲量加上管理標題的內存塊就脫離 MBREAK ,然後MBREAK就更新。這一過程反復進行,直至系統使一個內存塊空閑,管理標題包含有該存儲塊的存儲量為止。此時,PFREE 通過頭上的鏈接表插入項被更新為指向該內存塊,而塊本身則用一個指向舊 PFREE 內容的指針進行更新,以建立一個鏈接表。下一次出現分配請求時,系統就會搜索空閑內存塊鏈接表,尋找適合請求存儲量的第一個空閑內存塊。一旦找到合適的內存塊,它將此內存塊分成兩部分,一部分返還給系統,另一部分則送回給自由表。最先適合內存分配演算法實現起來簡單,而且開始時很好用。但是,經過一段時間後,會出現如下的情況:當系統將內存交給自由表時,它會從自由表的開頭部分去掉大內存塊,插入剩餘的小內存塊。最先適合演算法實際上成了一個排序演算法,即把所有小內存碎片放在自由表的開頭部分。因此,自由表會變得很長,有幾百甚至幾千個元素。因此,內存分配變得時間很長又無法預測,大內存塊分配所花時間要比小內存塊分配來得長。另外,內存塊的無限制拆分使內存碎片程度很高。有些實現方法在使內存空閑時會將鄰近的空閑內存塊連接起來。這種方法多少有些作用,而最先適合演算法與時間共處演算法(time co-location)和空間共處演算法(spatial co-location)不同,它在使內存塊空閑時,無法提高相鄰內存塊同時空閑的概率。 最佳適合與最差適合分配程序 最佳適合演算法在功能上與最先適合演算法類似,不同之處是,系統在分配一個內存塊時,要搜索整個自由表,尋找最接近請求存儲量的內存塊。這種搜索所花的時間要比最先適合演算法長得多,但不存在分配大小內存塊所需時間的差異。最佳適合演算法產生的內存碎片要比最先適合演算法多,因為將小而不能使用的碎片放在自由表開頭部分的排序趨勢更為強烈。由於這一消極因素,最佳適合演算法幾乎從來沒有人採用過。最差適合演算法也很少採用。最差適合演算法的功能與最佳適合演算法相同,不同之處是,當分配一個內存塊時,系統在整個自由表中搜索與請求存儲量不匹配的內存快。這種方法比最佳適合演算法速度快,因為它產生微小而又不能使用的內存碎片的傾向較弱。始終選擇最大空閑內存塊,再將其分為小內存塊,這樣就能提高剩餘部分大得足以供系統使用的概率。夥伴(buddy)分配程序與本文描述的其它分配程序不同,它不能根據需要從被管理內存的開頭部分創建新內存。它有明確的共性,就是各個內存塊可分可合,但不是任意的分與合。每個塊都有個朋友,或叫「夥伴」,既可與之分開,又可與之結合。夥伴分配程序把內存塊存放在比鏈接表更先進的數據結構中。這些結構常常是桶型、樹型和堆型的組合或變種。一般來說,夥伴分配程序的工作方式是難以描述的,因為這種技術隨所選數據結構的不同而各異。由於有各種各樣的具有已知特性的數據結構可供使用,所以夥伴分配程序得到廣泛應用。有些夥伴分配程序甚至用在源碼中。夥伴分配程序編寫起來常常很復雜,其性能可能各不相同。夥伴分配程序通常在某種程度上限制內存碎片。固定存儲量分配程序有點像最先空閑演算法。通常有一個以上的自由表,而且更重要的是,同一自由表中的所有內存塊的存儲量都相同。至少有四個指針:MSTART 指向被管理內存的起點,MEND 指向被管理內存的末端,MBREAK 指向 MSTART 與 MEND 之間已用內存的末端,而 PFREE[n] 則是指向任何空閑內存塊的一排指針。在開始時,PFREE[*] 為 NULL,MBREAK 指針為 MSTART。當一個分配請求到來時,系統將請求的存儲量增加到可用存儲量之一。然後,系統檢查 PFREE[ 增大後的存儲量 ] 空閑內存塊。因為 PFREE[ 增大後的存儲量 ] 為 NULL,一個具有該存儲量加上一個管理標題的內存塊就脫離 MBREAK,MBREAK 被更新。這些步驟反復進行,直至系統使一個內存塊空閑為止,此時管理標題包含有該內存塊的存儲量。當有一內存塊空閑時,PFREE[ 相應存儲量 ] 通過標題的鏈接表插入項更新為指向該內存塊,而該內存塊本身則用一個指向 PFREE[ 相應存儲量 ] 以前內容的指針來更新,以建立一個鏈接表。下一次分配請求到來時,系統將 PFREE[ 增大的請求存儲量 ] 鏈接表的第一個內存塊送給系統。沒有理由搜索鏈接表,因為所有鏈接的內存塊的存儲量都是相同的。固定存儲量分配程序很容易實現,而且便於計算內存碎片,至少在塊存儲量的數量較少時是這樣。但這種分配程序的局限性在於要有一個它可以分配的最大存儲量。固定存儲量分配程序速度快,並可在任何狀況下保持速度。這些分配程序可能會產生大量的內部內存碎片,但對某些系統而言,它們的優點會超過缺點。 減少內存碎片 內存碎片是因為在分配一個內存塊後,使之空閑,但不將空閑內存歸還給最大內存塊而產生的。最後這一步很關鍵。如果內存分配程序是有效的,就不能阻止系統分配內存塊並使之空閑。即使一個內存分配程序不能保證返回的內存能與最大內存塊相連接(這種方法可以徹底避免內存碎片問題),但你可以設法控制並限制內存碎片。所有這些作法涉及到內存塊的分割。每當系統減少被分割內存塊的數量,確保被分割內存塊盡可能大時,你就會有所改進。這樣做的目的是盡可能多次反復使用內存塊,而不要每次都對內存塊進行分割,以正好符合請求的存儲量。分割內存塊會產生大量的小內存碎片,猶如一堆散沙。以後很難把這些散沙與其餘內存結合起來。比較好的辦法是讓每個內存塊中都留有一些未用的位元組。留有多少位元組應看系統要在多大程度上避免內存碎片。對小型系統來說,增加幾個位元組的內部碎片是朝正確方向邁出的一步。當系統請求1位元組內存時,你分配的存儲量取決於系統的工作狀態。如果系統分配的內存存儲量的主要部分是 1 ~ 16 位元組,則為小內存也分配 16 位元組是明智的。只要限制可以分配的最大內存塊,你就能夠獲得較大的節約效果。但是,這種方法的缺點是,系統會不斷地嘗試分配大於極限的內存塊,這使系統可能會停止工作。減少最大和最小內存塊存儲量之間內存存儲量的數量也是有用的。採用按對數增大的內存塊存儲量可以避免大量的碎片。例如,每個存儲量可能都比前一個存儲量大 20%。在嵌入式系統中採用「一種存儲量符合所有需要」對於嵌入式系統中的內存分配程序來說可能是不切實際的。這種方法從內部碎片來看是代價極高的,但系統可以徹底避免外部碎片,達到支持的最大存儲量。將相鄰空閑內存塊連接起來是一種可以顯著減少內存碎片的技術。如果沒有這一方法,某些分配演算法(如最先適合演算法)將根本無法工作。然而,效果是有限的,將鄰近內存塊連接起來只能緩解由於分配演算法引起的問題,而無法解決根本問題。而且,當內存塊存儲量有限時,相鄰內存塊連接可能很難實現。有些內存分配器很先進,可以在運行時收集有關某個系統的分配習慣的統計數據,然後,按存儲量將所有的內存分配進行分類,例如分為小、中和大三類。系統將每次分配指向被管理內存的一個區域,因為該區域包括這樣的內存塊存儲量。較小存儲量是根據較大存儲量分配的。這種方案是最先適合演算法和一組有限的固定存儲量演算法的一種有趣的混合,但不是實時的。有效地利用暫時的局限性通常是很困難的,但值得一提的是,在內存中暫時擴展共處一地的分配程序更容易產生內存碎片。盡管其它技術可以減輕這一問題,但限制不同存儲量內存塊的數目仍是減少內存碎片的主要方法。現代軟體環境業已實現各種避免內存碎片的工具。例如,專為分布式高可用性容錯系統開發的 OSE 實時操作系統可提供三種運行時內存分配程序:內核 alloc(),它根據系統或內存塊池來分配;堆 malloc(),根據程序堆來分配; OSE 內存管理程序 alloc_region,它根據內存管理程序內存來分配。從 許多方面來看,Alloc就是終極內存分配程序。它產生的內存碎片很少,速度很快,並有判定功能。你可以調整甚至去掉內存碎片。只是在分配一個存儲量後,使之空閑,但不再分配時,才會產生外部碎片。內部碎片會不斷產生,但對某個給定的系統和八種存儲量來說是恆定不變的。Alloc 是一種有八個自由表的固定存儲量內存分配程序的實現方法。系統程序員可以對每一種存儲量進行配置,並可決定採用更少的存儲量來進一步減少碎片。除開始時以外,分配內存塊和使內存塊空閑都是恆定時間操作。首先,系統必須對請求的存儲量四捨五入到下一個可用存儲量。就八種存儲量而言,這一目標可用三個 如果 語句來實現。其次,系統總是在八個自由表的表頭插入或刪除內存塊。開始時,分配未使用的內存要多花幾個周期的時間,但速度仍然極快,而且所花時間恆定不變。堆malloc() 的內存開銷(8 ~ 16 位元組/分配)比 alloc小,所以你可以停用內存的專用權。malloc() 分配程序平均來講是相當快的。它的內部碎片比alloc()少,但外部碎片則比alloc()多。它有一個最大分配存儲量,但對大多數系統來說,這一極限值足夠大。可選的共享所有權與低開銷使 malloc() 適用於有許多小型對象和共享對象的 C++ 應用程序。堆是一種具有內部堆數據結構的夥伴系統的實現方法。在 OSE 中,有 28 個不同的存儲量可供使用,每種存儲量都是前兩種存儲量之和,於是形成一個斐波那契(Fibonacci)序列。實際內存塊存儲量為序列數乘以 16 位元組,其中包括分配程序開銷或者 8 位元組/分配(在文件和行信息啟用的情況下為 16 位元組)。當你很少需要大塊內存時,則OSE內存管理程序最適用。典型的系統要把存儲空間分配給整個系統、堆或庫。在有 MMU 的系統中,有些實現方法使用 MMU 的轉換功能來顯著降低甚至消除內存碎片。在其他情況下,OSE 內存管理程序會產生非常多的碎片。它沒有最大分配存儲量,而且是一種最先適合內存分配程序的實現方法。內存分配被四捨五入到頁面的偶數——典型值是 4 k 位元組。(T111)
9、怎樣設計一個內存池,減少內存碎片?
一般工程里不推薦你寫,因為你費力寫一個出來99%可能性沒有內置的好,且內存出bug難調試
一定要設計的話,你也可以當個玩具寫寫玩玩:
1. 實現教科書上的內存分配器:
做一個鏈表指向空閑內存,分配就是取出一塊來,改寫鏈表,返回,釋放就是放回到鏈表裡面,並做好歸並。注意做好標記和保護,避免二次釋放,還可以花點力氣在如何查找最適合大小的內存快的搜索上,減少內存碎片,有空你了還可以把鏈表換成夥伴演算法,寫著玩嘛。
2. 實現固定內存分配器:
即實現一個 FreeList,每個 FreeList 用於分配固定大小的內存塊,比如用於分配 32位元組對象的固定內存分配器,之類的。每個固定內存分配器裡面有兩個鏈表,OpenList 用於存儲未分配的空閑對象,CloseList用於存儲已分配的內存對象,那麼所謂的分配就是從 OpenList 中取出一個對象放到 CloseList 里並且返回給用戶,釋放又是從 CloseList 移回到 OpenList。分配時如果不夠,那麼就需要增長 OpenList:申請一個大一點的內存塊,切割成比如 64 個相同大小的對象添加到 OpenList中。這個固定內存分配器回收的時候,統一把先前向系統申請的內存塊全部還給系統。
3. 實現 FreeList 池:
在你實現了 FreeList的基礎上,按照不同對象大小(8位元組,16位元組,32,64,128,256,512,1K。。。64K),構造十多個固定內存分配器,分配內存時根據內存大小查表,決定到底由哪個分配器負責,分配後要在頭部的 header 處(ptr[-sizeof(char*)]處)寫上 cookie,表示又哪個分配器分配的,這樣釋放時候你才能正確歸還。如果大於64K,則直接用系統的 malloc作為分配,如此以浪費內存為代價你得到了一個分配時間近似O(1)的內存分配器,差不多實現了一個 memcached 的 slab 內存管理器了,但是先別得意。此 slab 非彼 slab(sunos/solaris/linux kernel 的 slab)。這說白了還是一個弱智的 freelist 無法歸還內存給操作系統,某個 FreeList 如果高峰期佔用了大量內存即使後面不用,也無法支援到其他內存不夠的 FreeList,所以我們做的這個和 memcached 類似的分配器其實是比較殘缺的,你還需要往下繼續優化。
4. 實現正統的 slab (非memcached的偽 slab)代替 FreeList:
這時候你需要閱讀一下論文了,現代內存分配技術的基礎,如何管理 slab 上的對象,如何進行地址管理,如何管理不同 slab 的生命周期,如何將內存回收給系統。然後開始實現一個類似的東西,文章上傳統的 slab 的各種基礎概念雖然今天沒有改變,但是所用到的數據結構和控制方法其實已經有很多更好的方法了,你可以邊實現邊思考下,實在不行還可以參考 kernel 源碼嘛。但是有很多事情應用程序做不了,有很多實現你是不能照搬的,比如頁面提供器,可以提供連續線性地址的頁面,再比如說 kernel 本身記錄著每個頁面對應的 slab,你查找 slab 時,系統其實是根據線性地址移位得到頁面編號,然後查表得到的,而你應用程序不可能這么干,你還得做一些額外的體系來解決這些問題,還需要寫一些額外的 cookie 來做標記。做好內存收縮工作,內存不夠時先收縮所有分配器的 slab,再嘗試重新分配。再做好內存回收工作,多餘的內存,一段時間不使用可以還給操作系統。
5. 實現混合分配策略:
你實現了上面很多常見的演算法後,該具體閱讀各種內存分配器的代碼了,這些都是經過實踐檢驗的,比如 libc 的內存分配器,或者參考有自帶內存管理的各種開源項目,比如 python 源碼,做點實驗對比他們的優劣,然後根據分配對象的大小採用不同的分配策略,區別對待各種情況。試驗的差不多了就得引入多線程支持了,將你的鎖改小。注意很多系統層的線程安全策略你是沒法弄的,比如操作系統可以關中斷,短時間內禁止本cpu發生任務切換,這點應用程序就很麻煩了,還得用更小的鎖來代替。當鎖已經小到不能再小,也可以選擇引入 STM 來代替各種鏈表的鎖。
6. 實現 Per-CPU Cache:
現代內存分配器,在多核下的一個重要優化就是給多核增加 cache,為了進一步避免多線程鎖競爭,需要引入 Per-CPU Cache 了。分配內存先找到對應線程所在的cpu,從該cpu上對應的 cache 里分配,cache 不夠了就一次性從你底層的內存分配器里多分配幾個對象進來填充 cache,釋放時也是先放回 cache,cache裡面如果對象太多,就做一次收縮,把內存換個底層分配器,讓其他 cpu 的cache有機會利用。這樣針對很多短生命周期的頻繁的分配、釋放,其實都是在 cache 里完成的,沒有鎖競爭,同時cache分配邏輯簡單,速度更快。操作系統裡面的代碼經常是直接讀取當前的cpu是哪個,而應用層實現你可以用 thread local storage 來代替,目前這些東西在 crt的 malloc 里還暫時支持不到位(不排除未來版本會增加),可以更多參考 tc/jemalloc。
7. 實現地址著色:
現代內存分配器必須多考慮匯流排壓力,在很多機型上,如果內存訪問集中在某條 cache line相同的偏移上,會給匯流排帶來額外的負擔和壓力。比如你經常要分配一個 FILE 對象,而每個 FILE對象使用時會比較集中的訪問 int FILE::flag; 這個成員變數,如果你的頁面提供器提供的頁面地址是按照 4K對齊的,那麼很可能多個 FILE對象的 flag 成員所處的 cache line 偏移地址是相同的,大量訪問這些相同的偏移地址會給匯流排帶來很大負擔,這時候你需要給每個對象額外增加一些偏移,讓他們能夠均勻的分布在線性地址對應的cache line 偏移上,消減匯流排沖突的開銷。
8. 優化緩存競爭:
多核時代,很多單核時代的代碼都需要針對性的優化改寫,最基本的一條就是 cache 競爭,這是比前面鎖競爭更惡劣的情況:如果兩個cpu同時訪問相同的 cache-line 或者物理頁面,那麼 cpu 之間為了保證內存一致性會做很多的通信工作,比如那個cpu0需要用到這段內存,發現cpu1也在用,那麼需要通知cpu1,將cpu1 L1-L2緩存裡面的數據寫回該物理內存,並且釋放控制權,這時cpu0取得了控制權才能繼續操作,期間cpu0-cpu1之間的通信協議是比較復雜的,代價也是比較大的,cache競爭比鎖競爭惡劣不少。為了避免 cache 競爭,需要比先前Per-CPU cache 更徹底的 Per-CPU Page 機制來解決,直接讓不同的cpu使用不同的頁面進行二次分配,徹底避免 cache 競爭。具體應用層的做法也是利用線性地址來判斷所屬頁面(因為物理頁面映射到進程地址也是4k對齊的),同時繼續使用 thread local storage 或者用系統提供的 api 讀取當前屬於哪個 cpu 來實現。為了避免核太多每個核占據大量的頁面帶來的不必要的浪費,你可以參考下 Linux 最新的 slub 內存分配演算法,但是 slub 也有未盡之處,好幾個 linux 發行版在實踐中發現 slub 還是存在一些問題的(非bug,而是機制),所以大部分發行版默認都是關閉 slub 的,雖然,你還是可以借鑒測試一下。
9. 調試和折騰:
繼續參考各種現代內存分配器,取長補短,然後給你的分配器添加一些便於調試的機制,方便診斷各種問題。在你借鑒了很多開源項目,自己也做了一些所謂的優化,折騰了那麼久以後,你或許以為你的分配器可以同各種開源分配器一戰了,測試效果好像也挺好的,先別急,繼續觀察內存利用率,向操作系統申請/歸還內存的頻率等一系列容易被人忽視的指標是否相同。同時更換你的測試用例,看看更多的情況下,是否結果還和先前一樣?這些都差不多的時候,你發現沒有個一兩年的大規模持續使用,你很難發現一些潛在的隱患和bug,可能你覺得沒問題的代碼,跑了兩年後都會繼續報bug,這很正常,多點耐心,興許第三年以後就比較穩定了呢?
10、phpcms如何添加碎片
在模板中直接插入 {block('news', 1)} 格式的碎片標簽,然後進後台模板管理點擊「可視化」即可看到添加碎片的鏈接。
碎片參數說明:
1、第一個參數「news」是頁面唯一標識,您可以自己命名;
2、第二個參數「1」是當前頁面的碎片序號,為正整數,保證同一頁面不重復即可
如何新建碎片
1、選中增加碎片的模板,在要添加的位置寫上類似如下{pc:block pos="chuangxinji_index_left"}{/pc}的語句。 "chuangxinji_index_left" 為頁面的標識,第二個參數可以省略。
2、在後台的內容-》內容發布管理-》碎片管理,點擊下內容頁(如果添加的碎片在內容頁,就點擊內容頁,如果在首頁,就點擊首頁)。在新跳轉的頁面中,在添加碎片的位置會出現「碎片:碎片名」 。點擊這個碎片,點擊 添加碎片,在碎片配置好後點擊確定。
3、刷新一下,在碎片管理里,會出現設置好的碎片 chuangxinji_index_left ,點擊「維護內容」那個圖標,然後在編輯框里對碎片進行編輯。