【问题标题】:Memory limit exceeded with PHP foreachPHP foreach 超出内存限制
【发布时间】:2012-05-05 05:57:43
【问题描述】:

我正在尝试遍历数组中的数千个项目,执行一些操作并将一些值保存到 mysql 表中。

但是,当我遍历时,内存使用量会不断增长,直到我们用完我在 php.ini 中指定的内存,这非常快。

我尝试使用 unset,将变量设置为 null 并查看垃圾收集,但没有任何影响。

有没有更有效的方法可以循环遍历这些元素(即内存使用量不会持续增长)。

下面是我正在做的一个简化示例。

foreach ($subscribers as $subscriber)
{
    $member = new Member($subscriber['id']);
    if ($member['id'] > 0)
    {
        $bulletin = Bulletin::getCustomBulletin($member['id']);

        Bulletin::compileBulletin($member['email'], time(), $bulletin['title'], $bulletin['content']);

        echo $member['email'] . "\n";
        echo memory_get_usage() . "\n";
    }
}

这会产生以下结果:

an@email.com
11336688
an@email.com
12043640
an@email.com
12749952

【问题讨论】:

  • 你的 php.ini 中的内存限制是多少?
  • 我已将它从 32MB 移动到 512MB。我目前只在我的本地机器上进行测试。我知道在 php.ini 中做出的任何合理选择都太小了。
  • 您是否尝试过将 Member 对象实例化替换为仅返回所需详细信息的静态查找函数?您甚至可以尝试用静态数组替换该行,看看是否还有问题。
  • 不要全部加载到数组中($subscribers)。使用unbuffer query 或XMLreaders 进行大数据处理。

标签: php memory memory-leaks


【解决方案1】:

$suscribers 是数据库查询的结果吗?

如果是这样,它可能是您的问题的根源:行将被缓冲在内存中,即使您一次浏览它们。

您可以尝试使用unbuffered queries 或限制查询的结果数量并执行几个较小的查询。

另见:Why "Allowed memory size exhausted"?

【讨论】:

  • 是的。不过,它作为一项操作并不是特别昂贵,即只为每个用户提取一个 id 键和一个 id。您认为执行几个较小操作的好方法是什么?我尝试了一个 for 循环,在该循环中我更新了每个循环的偏移量和限制,但这并没有改善内存管理。
  • @AndyTait 是的,这就是我的建议,如果它不起作用,那么可能不是它(它在类似的问题上对我有用)。您是否获取了 很多$suscribers
  • 也许试试无缓冲查询?
【解决方案2】:

您的foreach 循环没有任何可疑之处(没有要取消设置的变量,因为它们都在每次迭代中被覆盖)。您将需要找出导致内存占用的行。在专业 IDE 中找到的分析器会对此有所帮助。如果您没有访问权限,您可以像以前一样使用memory_get_usage(),但将其放在每一行之后,以检查是哪一个导致了瓶颈。

还有免费的分析工具,例如xdebug

【讨论】:

  • 我已经安装了 xdebug 和 webgrind。不幸的是,它似乎没有显示哪些功能花费的时间最多。我怀疑我需要找到一种完全实现这一目标的新方法,即处理一组较小的数据。
【解决方案3】:

不幸的是,从您发布的代码中很难判断出 Member 对象在内部做什么,但这看起来是可能的:

recursive references leak memory

我将在上面的代码中消除新成员对象的创建,以检查这是否是内存泄漏的根源。根据您的说法,无论如何创建成员对象可能都是不必要的,可以将其替换为静态查找成员函数。

【讨论】:

  • 问题是,在 Bulletin::getCustomBulletin() 和 Bulletin::compileBulletin() 中有数百行代码。所以我认为我需要找到一种方法来杀死循环每次迭代后吞噬的内存。
  • 我认为这些都很好,但是如果在 Member 类中有对其他对象的任何引用,那么当您在循环中创建新成员对象时,内存将不会被释放。值得在 Member 类中编写一个静态成员函数来获取循环所需的详细信息,例如Member::getMemberDetails($subscriber['id']).Static 调用应该没问题,这就是 Bulletin 类不需要被触及的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 2019-11-07
相关资源
最近更新 更多