該函數傳遞約70k個對象進行處理。加載數組並沒有問題,並且在它失敗之前它會經歷大約一半的迭代。內存限於ini_set('memory_limit','465M');
(雲服務)。它總是在$googleFunction
:/app/vendor/googleads/googleads-php-lib/src/Google/Api/Ads/Common/Lib/AdsSoapClient.php
中失敗。如何在PHP中優化此功能的內存使用情況?
我已經嘗試傳遞數組作爲基準,切換從array_chunk
到使用array_slice
在一個時間,通過引用傳遞$chunk
到$googleFunction
,在端部取消設置$chunk
,並調用在每次迭代之後gc_collect_cycles()
。
它又如何失敗?必須有某處存在內存泄漏,但除$chunk
和$result
之外沒有大的分配,並且在每次迭代過程中調用的每個函數都會超出範圍,因此可能分配的任何內容都應該被垃圾收集。我覺得它可能與函數引用有關。經過約四分之一的迭代後,它使用240M。它每次迭代增長約10M。
function googleJob($job = null, &$list, $googleFunction, $before = null) {
// initialize $total, $gaw, $processed
for ($chunkIndex = 0; $chunkIndex < count($list); $chunkIndex += 2000) {
echo '3:'.memory_get_usage().' ';
$chunk = array_slice($list, $chunkIndex, 2000); # limit of 2000/request
echo '4:'.memory_get_usage().' ';
if ($before) {
foreach ($chunk as $item) {
$before($item); # function reference
}
}
echo '5:'.memory_get_usage().' ';
$i = 0; // try harder to make Google work
$result = null;
do {
try {
$result = $gaw->$googleFunction($chunk);
echo '6:'.memory_get_usage().' ';
} catch (\SoapFault $e) { # try to remove the bad items and try again
// no errors generated
}
} while ($result == null && $chunk); // Retry the request if not empty
array_walk($chunk, function($item) { $item->save(); });
echo '7:'.memory_get_usage().' ';
$processed += count($chunk);
if ($job) {
$job->progress = round($processed/$total * 100);
$job->save() or Yii::error($job->getErrors());
}
echo '8:'.memory_get_usage().' ';
unset($chunk);
unset($result);
echo '9:'.memory_get_usage().'... ';
gc_collect_cycles();
echo memory_get_usage().PHP_EOL;
}
}
記憶 '剖析' 輸出:
3:110267832 4:110372112 5:111920328 6:123908368 7:129432080 8:129432080 9:121662520... 121662520
3:121662520 4:121766800 5:123281704 6:138001000 7:143493888 8:143493888 9:135745264... 135745264
可能會或可能不會與此有關,但[本博客](http://blog.ircmaxell.com/2014/ 12/what-about-garbage.html)詳細描述了垃圾收集的功能。我從來沒有想過告訴PHP如何管理內存。它的設計並非如此。 – Machavity
您可能想考慮使用隊列來處理這些作業 – FuzzyTree
將'$ list'作爲參考傳遞沒有意義,因爲您從不修改該數組。 – Barmar