PHP:在循环中遇到一些内存问题

时间:2015-08-06 18:57:58

标签: php memory garbage-collection

我有一个类似于以下内容的代码段:

while (true) {
    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();
    ...
    $myArray = [1, 2, 3];
    ...
    sleep(1); //Sleep at the end, to save CPU.
}

此代码段应作为守护程序服务运行,但我在执行此操作时遇到了很多麻烦。

问题:每次迭代都会增加进程内存使用量。好像在每次新的迭代中都要实例化一个新的$myObject,但前一个仍在内存中分配,等等。

我试过了:

  • unset循环结束时的所有变量(就在sleep()之前)。
  • 将所有变量设置为null
  • 将它们封装在单独的函数(while (true) { doThis(); }
  • 手动拨打gc_collect_cycles()

这些都没有减少内存使用量。

我不知道如何强制释放所有内存。

6 个答案:

答案 0 :(得分:6)

我在这里的答案中编写了我之前的评论。这并不能完全解释如何释放已分配的内存,但会引导您通过一种方式来发现应用程序中的内容。有了它,您可以优化代码。

查找内存使用瓶颈通常是一项具有挑战性的任务。您可以从查看与I / O相关的调用开始,例如数据库查询,文件访问甚至网络。除了增加执行时间,有时这些操作可以分配一些内存。

如果您已经从I / O操作返回的资源中清除了内存,并且没有注意到分配的内存大幅减少,则下一步可能是使用Blackfire等工具分析您的应用程序(https://blackfire.io/ )。

Blackfire将为您提供每个函数调用的详细视图及其有关内存,CPU和执行时间的统计信息。利用该数据,可以检查哪些操作正在分配过多的存储器。当您将鼠标指针放在通话详细信息中的内存条上时,可以找到此信息,如下所示:http://i.imgur.com/762mWmF.png

答案 1 :(得分:4)

经过对该主题的大量研究后,我终于确信没有办法手动强制释放内存或强制对象破坏。

但是,有些东西帮助我降低了内存使用率(绝对无法阻止无限内存堆叠)是为了意识到PHP中存在无循环范围并且切换时发生了垃圾收集范围。

在C#或Java中,只能在循环内访问在while (...) {}内创建的变量。这不是PHP的标准。在$myObject指令中创建的while可在整个应用程序中访问。

这意味着所提供的代码段将更好地呈现为:

while (true) {
    myFunc();
}

function myFunc()
{
    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();
    ...
    $myArray = [1, 2, 3];
    ...
    sleep(1); //Sleep at the end, to save CPU.
}

在函数中封装逻辑会强制更改范围,这意味着将在每次迭代时调用垃圾收集器。这有解决了我的问题,但它在某种程度上降低了我的RAM使用率。

我从这次经历中学到的是,PHP可能不适合这个特定的项目要求。我需要更多的内存控制,而且PHP不能对创建/销毁的对象提供任何控制。某些本机函数不能正确释放内存(特别是那些执行I / O,数据库访问和memcached的内存)。

答案 2 :(得分:2)

很可能(提供给出的信息)仍然存在对创建的对象的引用,这些引用阻止垃圾收集器从内存中removing the object。由于它基本上是在计算引用,因此确保不存储引用,通过复制值或小心地取消它们,可以避免这种情况。

通常情况下,使用while(true)构造时不会因为这个原因而精确地创建对象并使其尽可能自包含,以确保实际上不会发生内存泄漏。

我知道这个答案并没有直接帮助(我没有足够的代表对这个问题发表评论),但它可能会让你走上正轨。

答案 3 :(得分:1)

我最好的猜测(由于缺乏对所涉及类的内部的了解),要么是将其他对象分配为其属性(或者可能是自引用),要么使用数组(因为代码示例)类似于真实案例)有自己的引用,如果数组的大小很重要,它将解释内存泄漏。

如果有任何帮助,请查看php.net的参考计数基础:

http://php.net/manual/en/features.gc.refcounting-basics.php

答案 4 :(得分:0)

正如上面提到的@ m1lt0n和@MartPluijmaekers,这可能是与对象引用相关的问题。

我们不知道您的Class()班级和getSomeOtherObj()方法中有什么内容,所以我无法肯定地说出任何内容,但是下面的代码段可能会帮助您弄清楚是否是案件与否。

/* Here is your Class */
class Class {

    public function __construct () {
        $this->child = new AnotherClass( $this );
    }

    /* This is what you have to have ... */
    protected function __destruct () {
        unset( $this->child );
    }
}

/* Here is the other class  */
class AnotherClass {

    public function __construct ( $parent ) {
        $this->parent = $parent;
    }
}

/* Infinite Loop */
while ( true ) {

    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();

    $myArray = [1, 2, 3];

    /* manually destroying the object */
    $myObject->__destruct();
    unset( $myObject );

    /* rest of free-ing goes here ... */

    sleep(1); //Sleep at the end, to save CPU.
}

该片段应该是不言自明的。

答案 5 :(得分:0)

问题是你处于一个无限循环中,在请求时没有结束。 PHP的垃圾收集器旨在在请求结束时处理,否则用户无法访问它。 PHP被设计为被调用和丢弃,而不是无限期地保持活着。因此它无法修复。所以,我建议创建一个chron作业,定期重启php循环,从而结束请求并释放内存。有关详细信息,请参阅this document