2011-08-18 52 views
6

在調試耗盡內存的代碼時,我發現了一個非常有趣的問題,最重要的是我不知道如何解決這個問題。通過引用自身破壞對象

該應用程序大致由單個Survey對象組成,其中包含多個Question對象。 Question對象包含對他們所在調查的引用,例如,需要從其他問題獲取答案。


下面的循環是造成內存溢出:

foreach ($survey_ids_arr as $survey_id) { 
    $Survey = new Survey($survey_id); 
} 

真的沒什麼異國情調的調查構造正在發生的事情;

  • 從數據庫
  • 從數據庫
  • 獲取的所有問題的屬性創建每一個問題一個問題對象獲取其屬性(傳遞一個參考$這個)
  • 將所有問題對象的內部陣列

並且從查看代碼,您會說在每次迭代中,由於$ Survey變量被覆蓋,所以對象從內存中清除。對??錯誤:)

隨着腳本經過循環,內存堆積 - 調用顯示調查對象使用的內存未按預期釋放,此時另一個對象被分配給$Survey變量。即使在循環結束時調用unset($Survey)也不會釋放內存。


罪魁禍首是傳遞給在創建問題的對象$this引用。這些引用防止對象從內存中清除 - 作爲php.net狀態手冊:

析構函數方法將被調用,一旦到某個對象的所有引用都被刪除

所以什麼阻止對象被清理,是它對自身的引用。很好,嗯? :)

所以,問題是我的對象是一個記憶殺手。不幸的是,我想不出一個解決方案(除了寫一個醜陋的方法來清除問題並從循環中調用它)。 Survey中的析構函數不是一個選項;如上所述,這不叫,因爲 Question對象仍然有引用。

任何想法?有人必須已經遇到了這個問題 - 含父項的子對象並不是一個不常見的體系結構,是嗎?

+0

問題是你的設計中存在一個矛盾:你想阻止對象從內存中清除,但你想清除它? – m0skit0

+1

在php 5.3中解決了這個問題。你使用什麼PHP版本? – J0HN

+0

你是對的J0HN,我剛剛發現了錯誤報告! https://bugs.php.net/bug.php?id=33595 – Rijk

回答

2

所以,這裏是答案:切換到PHP 5.3,因爲這個問題已經在resolved裏面了。如果您必須使用PHP < 5.3.0,則有責任釋放在循環引用中捕獲的對象。一種可能的辦法是引入特殊方法,將剝去兒童物品的鏈接以允許它們被收集。GC

P.S.也可能對某人有用,Doctrine 1.2在模型中有這樣的鏈接,因此它是內存泄漏的主題,特別是如果您從數據庫中獲取大量實體。如果您遇到了這個問題,請嘗試(1)減少獲取的實體數量(例如,以數組的形式進行水合),(2)處理具有原始sql請求的實體。