2013-04-13 28 views
5

運行這個簡單的腳本時,我得到了下面的輸出結果。 它使我認爲我的代碼或Zend Framework/Magento堆棧中存在內存泄漏。迭代任何種類的Magento集合時會發生此問題。 有什麼我失蹤或做錯了?Magento/Zend Framework中的內存泄漏

腳本:

$customersCollection = Mage::getModel('customer/customer')->getCollection(); 

foreach($customersCollection as $customer) { 
    $customer->load(); 
    $customer = null; 
    echo memory_get_usage(). "\n"; 
} 

輸出:

102389104 
102392920 
... 
110542528 
110544744 
+0

@IMSoP確實... –

+0

這是另一個[參考](http://ringsdorff.net/2009/07/23/guest-post-fix-for-memory-leaks-in-magento)我發現。它看起來像問題駐留在循環引用。 – osondoar

+0

@osondoar如果您至少使用PHP 5.3(您現在應該知道),則循環引用將被垃圾收集器捕獲,儘管不是立即。但是,請參閱我的答案,爲什麼您的示例不會釋放非循環引用。 – IMSoP

回答

7

你的問題是,你正在發行與每次迭代,當你可以通過收集查詢加載必要的數據相當昂貴的查詢:

$collection = Mage::getResourceModel('customer/customer_collection')->addAttributeToSelect('*'); 

會做同樣的,但都在一個查詢。對此方法的警告是,如果有任何自定義事件觀察者爲customer_load_beforecustomer_load_after事件(這些事件沒有核心觀察者),則需要爲每個數據模型手動運行觀察者。

編輯:信貸osonodoar爲察覺了不正確的類引用(客戶/顧客VS客戶/ customer_collection)

3

爲一個對象(或其它值)的存儲器,當沒有提及它在PHP過程的任何地方只能被釋放。在你的情況下,行$customer = null只會將該對象的引用數量減少1,但不會達到零。

如果你考慮一個簡單的循環,這可能會變得更加清晰:

$test = array('a' => 'hello'); 
foreach ($test as $key => $value) 
{ 
    // $value points at the same memory location as $test['a'] 
    // internally, that "zval" has a "refcount" of 2 

    $value = null; 
    // $value now points to a new memory location, but $test['a'] is unnaffected 
    // the refcount drops to 1, but no memory is freed 
} 

因爲你正在使用的對象,存在添加的扭曲 - 你可以修改循環內的對象,而無需創建它的一個新副本:

$test = array('a' => new __stdClass); 
// $test['a'] is an empty object 

foreach ($test as $key => $value) 
{ 
    // $value points at the same object as $test['a'] 
    // internally, that object has a "refcount" of 2 

    $value->foo = "Some data that wasn't there before"; 
    // $value is still the same object as $test['a'], but that object now has extra data 
    // This requires additional memory to store that object 

    $value = null; 
    // $value now points to a new memory location, but $test['a'] is unnaffected 
    // the refcount drops to 1, but no memory is freed 
} 

// $test['a']->foo now contains the string assigned in the loop, consuming extra memory 

在你的情況下,該方法->load()據推測膨脹的數據量中的每個的$customersCollection依次成員,需要爲每個更多的內存。循環前後檢查$customersCollection可能會證實這一點。

0

首先,當unsetting變量使用unset($ variable)而不是$ variable = null時。它本質上是一樣的,但對於你的意圖要清楚得多。其次,PHP意味着死亡 - 內存泄漏並不是一個大問題,因爲PHP請求可能持續幾秒鐘,然後進程就會死亡,並且所有正在使用的內存都將被釋放以用於下一個請求。除非您遇到擴展問題,否則無需擔心。

編輯:這並不是說不要擔心代碼的質量,但對於這樣的事情來說,它最有可能不值得嘗試阻止它發生,除非它引起問題。

+0

感謝您的評論。這裏的主要問題是,當遍歷50000多個客戶時,會出現內存分配問題。例如,它將內存限制設置爲512Mb,腳本將崩潰 – osondoar

0

了處理內存泄漏的另一種方法是循環內調用exec,讓那exec函數做導致內存泄漏的作業部分。

所以一旦它完成了它的部分並終止了該exec中的所有內存泄漏將被釋放。

因此,通過巨大的迭代,這種不斷增加的內存丟失將被照顧。

0

@bencks響應在這裏是正確的方法,因爲在循環中調用load()是非常非常昂貴的調用。

調用$ customer-> load()將遞增地分配內存,這將被$ customersCollection引用,該內存不會在循環結束之前釋放。但是,如果因爲任何原因需要調用load(),則下面的代碼不會泄漏內存,因爲GC在每次迭代中釋放模型分配的所有內存。

$customersCollection = Mage::getModel('customer/customer')->getCollection(); 

foreach($customersCollection as $customer) { 
    $customerCopy = Mage::getModel('customer/customer')->load($customer->getId()); 

    //Call to $customerCopy methods 

    echo memory_get_usage(). "\n"; 
} 
+1

您的代碼泄漏給我。添加'$ customerCopy-> clearInstance();'雖然有竅門。 – benmarks

+0

嗯...當我執行它看起來像有泄漏但GC可以每隔5秒左右釋放一次內存,使內存消耗保持在一定的水平。不同的PHP/Magento配置可能會解釋爲什麼我們會得到不同的結果。無論如何,你的迴應是要走的路,謝謝! – osondoar