2011-10-22 86 views
13

假設我有一個構造函數的指針一大塊內存PTR和大小N.如果我做很多隨機的分配和各種尺寸的解除分配的內存池對象,我可以在這樣的狀態下得到的內存,我不能在內存中連續分配一個M字節的對象,即使可能有很多空閒!與此同時,我無法壓縮內存,因爲這會在消費者身上造成懸掛指針。在這種情況下,如何解決碎片問題?處理內存池中的碎片?

+0

你是想實現一個操作系統或至少其中的一部分?內存池比正常分配更受歡迎的唯一原因是正常分配涉及碎片。 – Dani

回答

8

我想添加我的2美分只是因爲沒有人指出你的描述聽起來像你正在實施一個標準的堆分配器(我。e當我們調用malloc()或operator new時,我們所有人都已經使用過)。

堆正是這樣一個對象,即進入虛擬內存管理器和大塊的內存要求(你所說的「一池」)。然後它有各種不同的算法來處理分配各種大小塊並釋放它們的最有效方式。此外,很多人多年來一直在修改和優化這些算法。長期以來,Windows帶有一個稱爲低碎片堆(LFH)的選項,您必須手動啓用該選項。從Vista開始LFH默認情況下用於所有堆。

堆並不完美,如果沒有適當的使用肯定可以拖垮性能。由於操作系統供應商無法預測您將使用堆的每種情況,因此他們的堆管理員必須針對「平均」使用進行優化。但是,如果你有一個條件類似於用於常規堆的要求(即多個對象,不同大小的....),你應該考慮只使用一個堆,而不是徹底改造它,因爲機會是你的實現將不如什麼操作系統已經爲你提供了。

內存分配,可以通過簡單地不使用堆提升性能的唯一時間是通過放棄一些其他方面(分配開銷,分配一輩子....),這是不是您的具體應用非常重要。

例如,在我們的應用中有要求的少很多分配比1KB但這些撥款只被用於時間(毫秒)非常短的時間。爲了優化應用程序,我用升壓池庫,但讓我的「分配器」實際上包含升壓池對象的集合,每個負責從16個字節分配一個特定尺寸可達1024(在4個步驟)擴展它。這提供了幾乎免費的(O(1)複雜性)分配/釋放這些對象,但美中不足的是,一個),內存使用率始終是大不降,即使我們沒有被分配一個單獨的對象,B)升壓游泳池從未釋放它所使用的內存(至少在我們使用它的模式下),所以我們只使用它來保存不會很長時間的對象。正常的內存分配的

所以這(些)方面,你是否願意在你的應用程序要放棄?

+1

非常好的解釋謝謝。 – user805547

6

根據不同的系統有幾個方法可以做到這一點。

儘量避免在第一時間碎片化,如果以2的冪分配塊,你也少了導致這種碎裂的機會。還有其他一些方法,但是如果你達到這個狀態,那麼你在這個時候只是OOM,因爲除了殺死請求內存的進程之外,沒有任何處理它的微妙方法,直到你可以分配內存,或者返回NULL作爲你的分配區域。

另一種方式是指針傳遞給您的數據的指針(例如:INT **)。然後,您可以重新安排程序下面的內存(我希望線程安全)並壓縮分配,以便您可以分配新塊並仍舊保留舊塊中的數據(一旦系統進入此狀態,雖然這會變成沉重的開銷,但應該很少做完了)。

也有「分箱」內存的方法,以便您有連續的頁面,例如專用1頁僅用於分配512和更少,另一個用於1024和更少等等......這使得更容易做出有關要使用哪個bin,在最壞的情況下,您從下一個最高的bin中拆分,或者從較低的bin合併,這樣可以減少跨多個頁面的碎片化概率。

0
  • 寫入池作爲分配列表進行操作,然後可以根據需要進行擴展和銷燬。這可以減少碎片。
  • 和/或實施分配轉移(或移動)的支持,所以你可以壓縮主動分配。該對象/持有者可能需要協助您,因爲該池可能不一定知道如何傳輸類型本身。如果池與集合類型一起使用,那麼完成壓縮/傳輸就容易得多。
3

對於您經常分配的對象實施object pools將顯着降低分段速度,而無需更改內存分配程序。

1

這將有助於更準確地知道你實際上想要做什麼,因爲有很多方法可以解決這個問題。
但是,第一個問題是:這是真的發生了嗎?還是理論上的問題?

有一點要記住的是,你通常有更多的虛擬內存地址空間比物理內存可用,所以即使當物理內存碎片,還有大量的連續虛擬內存。 (當然,下面的物理內存是不連續的,但是你的代碼看不到這些)。

我認爲有時候會有內存碎片的不必要的擔心,因此人們編寫一個自定義的內存分配器(或者更糟的是,他們用手柄和可移動的內存和壓縮來構造一個方案)。我認爲這些在實踐中很少需要,它有時可以提高性能,將其拋出並返回使用malloc。