【问题标题】:How do I optimize memory usage in PHP for this function?如何优化 PHP 中此函数的内存使用?
【发布时间】:2015-12-09 22:21:32
【问题描述】:

这个函数传递了大约 70k 个对象来处理。加载数组没有问题,并且在失败之前它完成了大约一半的迭代。内存限制为ini_set('memory_limit','465M');(云服务)。它总是在$googleFunction:/app/vendor/googleads/googleads-php-lib/src/Google/Api/Ads/Common/Lib/AdsSoapClient.php 中失败。

我已经尝试将数组作为引用传递,从array_chunk 切换到一次使用array_slice,通过引用$googleFunction 传递$chunk,取消设置$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

【问题讨论】:

  • 可能与这里无关,但this blog 详细描述了垃圾收集的作用。我从来没有尝试过告诉 PHP 如何管理内存。它并不是真正设计的那样。
  • 您可能需要考虑使用队列来处理这些作业
  • 传递$list 作为参考是没有意义的,因为您永远不会修改该数组。
  • $item-&gt;save() 是做什么的?这看起来像是函数中唯一会使用大量内存的部分,因为它将每个输入项都保存在某个地方。
  • 另外,$gaw-&gt;$googleFunction 是否将$chunk 保存在任何地方?

标签: php memory-management memory-leaks out-of-memory


【解决方案1】:

在我看来,您正在滥用肥皂服务。如果您告诉我们您的代码在 $googleFunction 处失败,我可以建议您为 $chunk 设置 100 或 200 个对象。

第二件事是 $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>";

} 

所以如果你有对象,我怀疑在 save() 函数中创建的 Active Record 对象不要忘记取消设置它们。 最简单的方法是像这样添加破坏:

function __destruct()
    {
        unset($this->bar);
    }

这应该会有所帮助

【讨论】:

  • Google recommends 2000,最多支持 5000 个。但我会一次尝试 100 个,并使用循环引用测试泄漏。好的,即使是 200 的小块,它仍然会泄漏。
  • 有趣的是,$save() 实际上确实会泄漏内存! foreach($agk as $v) { $v-&gt;save(); echo memory_get_usage().PHP_EOL; }
猜你喜欢
  • 1970-01-01
  • 2010-11-09
  • 2011-09-14
  • 2017-08-15
  • 2018-03-30
  • 2014-05-05
  • 1970-01-01
  • 1970-01-01
  • 2013-03-25
相关资源
最近更新 更多