如何在PHP中优化此功能的内存使用情况?
该函数传递约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
在我看来,你是滥用SOAP服务。如果您告诉我们您的代码在$ googleFunction失败,我可以为您提供$ 100或200个对象的$ chunk。第二件事是$ item-> save()函数。如果您有权访问课程,您应该检查是否有HAS-IS课程。唯一的地方PHP泄漏内存是这样的结构:
class Foo {
function __construct()
{
$this->bar = new Bar($this);
}
}
class Bar {
function __construct($foo = null)
{
$this->foo = $foo;
}
}
for($i = 0; $i < 10; $i++){
$foo = new Foo();
unset($foo);
echo number_format(memory_get_usage()) . "<br>";
}
所以如果你有对象,我怀疑活动记录的对象,在保存()函数创建不要忘了取消或修改它们。 简单的方法是添加毁灭这样的:
function __destruct()
{
unset($this->bar);
}
这应该有助于
[Google推荐2000](https://developers.google.com/adwords/api/docs/appendix/limits#general),并支持多达5000个。但我会一次尝试100个,并测试泄漏情况循环参考。即使有200个小块,它仍然会泄漏。 – Chloe
有趣的是''save()'确实会泄漏内存! 'foreach($ agk as $ v){ $ v-> save(); echo memory_get_usage()。PHP_EOL; }' – Chloe
好吧,我已经为Yii提交了一个错误报告。 https://github.com/yiisoft/yii2/issues/9679 – Chloe
可能会或可能不会与此有关,但[本博客](http://blog.ircmaxell.com/2014/ 12/what-about-garbage.html)详细描述了垃圾收集的功能。我从来没有想过告诉PHP如何管理内存。它的设计并非如此。 – Machavity
您可能想考虑使用队列来处理这些作业 – FuzzyTree
将'$ list'作为参考传递没有意义,因为您从不修改该数组。 – Barmar