2012-04-02 67 views
4

我有一個PHP腳本,它擁有大量的人員,它通過SOAP從外部資源抓取他們的細節,修改數據並將其發回。由於細節的大小,我將PHP的內存提升到了128MB。運行約4小時後(可能需要4天運行),內存耗盡。它用來做什麼繼承人的基本知識:垃圾回收器如何在PHP中工作

$people = getPeople(); 
foreach ($people as $person) { 
    $data = get_personal_data(); 
    if ($data == "blah") { 
     importToPerson("blah", $person); 
    } else { 
     importToPerson("else", $person); 
    } 
} 

後它跑的內存並撞車了,我決定了foreach循環之前數據初始化$根據top,該進程的內存使用情況並沒有7.8%以上上漲它已經運行了12個小時。

所以我的問題是,即使重用,PHP不會在循環內初始化的變量上運行垃圾回收器嗎?系統是否回收內存,PHP沒有將其標記爲可用,並最終會再次崩潰(我已經將其升級到了256MB,因此我已經更改了2件事情,並且不確定哪些修復了它,我可以更改我的腳本回答這個問題,但不想再等12個小時才能找出答案)?

我沒有使用Zend框架,所以像這樣的其他問題我不認爲是相關的。

編輯:我實際上沒有腳本的問題或它在做什麼。目前,就所有系統報告而言,我沒有任何問題。這個問題是關於垃圾收集器以及它如何/何時回收foreach循環中的資源和/或系統如何報告php進程的內存使用情況。

+2

我有興趣聽到爲什麼這已被低估了兩次投票現在... – Moses 2012-04-02 23:08:48

+0

'importToPerson()'會發生什麼? – PeeHaa 2012-04-02 23:12:01

+1

if($ data =「blah」){'be'if($ data ==「blah」){'? – PeeHaa 2012-04-02 23:12:26

回答

0

使用memory_get_usage()查看正在發生什麼?可以將它放在循環內部以查看內存分配中的行爲。 你有沒有試過看系統監視器或任何看到有多少內存PHP在這個過程中使用?

+0

不想修改腳本atm,因爲我們不得不從頭開始重新開始(已經是一天的工作)。最重要的是系統監視器和內存使用率沒有上升到7.8%以上,因此理論上沒有更多的內存分配和腳本不應該耗盡內存。 – Rudiger 2012-04-02 23:47:57

+0

而且你不能用這些修改來運行另一個腳本,並限制數字迭代?即我不完全確定你在做什麼。 – Norm 2012-04-02 23:50:34

+0

該腳本實際上是相當過程密集的,因爲它執行了大量的SOAP請求/數據庫操作。如果我無法得到答案,稍後我會進行調查,但是可能需要一天的時間更改腳本,運行一個小時左右,監視內存分配,沖洗和重複。希望有人比我更瞭解PHP垃圾收集器,以提供一些見解。 – Rudiger 2012-04-02 23:59:14

2

我不知道PHP的虛擬機的內部,但從我的經驗來看,它不會在您的頁面運行時進行垃圾收集。這是因爲它會在頁面完成時拋棄您的頁面創建的所有內容。

大多數情況下,當頁面內存不足並且限制非常高(而128Mb不高)時,會出現算法問題。許多PHP程序員組裝一個數據結構,然後將它傳遞給下一個迭代結構的步驟,通常會創建另一個結構。泡沫,沖洗,重複。不幸的是,這種方法是一個巨大的內存,你最終會在內存中創建多個數據副本。 PHP 5中兩個非常大的變化是對象被引用計數,而不是被複制,並且整個字符串子系統變得更快。但這仍然是一個問題。

爲了最大限度地減少內存使用,您需要重新構造算法,以便它可以從頭到尾處理一段數據。然後你得到下一個,然後重新開始。最好的情況是你永遠不會在內存中擁有整個數據集。對於數據庫支持的網站來說,這意味着在獲取下一個網站之前,處理從數據庫查詢一直到呈現的一行數據。當然,這種方法並不總是可行的,腳本只需要在內存中保存大量的數據。

也就是說,您可以對部分數據執行這種節省內存的方法。訣竅是,你明確地在循環結尾處有一個或兩個關鍵變量unset()。這應該回收空間。另一種「最佳實踐」的訣竅是將不需要在循環中的循環數據操作移出。你似乎已經發現了。

我已經運行了需要1Gb內存的PHP腳本。你可以設置每個腳本的內存限制,實際上,與ini_set('memory_limit', '1G');

+2

PHP 5.3增加了一個「真正的」垃圾收集器。它仍然不完善,但它是對你所描述的內容的改進。 – duskwuff 2012-04-02 23:58:41

+0

它實際上是在命令行上運行的。雖然我確實想過每次都做一行(而不是所有的行並遍歷返回的數組),但我認爲額外的數據庫查詢會否定它的任何好處。 – Rudiger 2012-04-03 00:24:32

+1

有一個巨大的循環來處理每一行是合理的,但是當您仍然在獲取之前的結果時,您可能很容易遇到嘗試執行新查詢的資源問題。而且,有時候執行大量小SQL查詢的速度會比重量更少。 – staticsan 2012-04-03 02:28:53