代码之家  ›  专栏  ›  技术社区  ›  thomasrutter

如何在PHP中修复内存泄漏

  •  17
  • thomasrutter  · 技术社区  · 15 年前

    我的PHP应用程序有一个导入脚本,可以导入记录。

    目前,它正在从csv文件导入。它读取csv文件的每一行,每次使用fgetcsv读取一行,并对它正在执行的每一行进行读取。 很多 处理该记录,包括数据库查询,然后转到下一行。它不需要不断积累更多的记忆。

    导入大约2500条记录后,php会死亡,并说它已经超过了内存限制(132MB左右)。

    csv文件本身只是两个meg——发生的另一个处理会进行大量的字符串比较、差异等。我在上面操作了大量的代码,很难找到“最小的复制样本”。

    找到和解决这样一个问题的好方法是什么?

    发现问题原因

    我有一个调试类,它记录运行时的所有数据库查询。所以这些SQL字符串,大约30kb长,保存在内存中。我意识到这不适合长时间运行的脚本。

    可能还有其他的记忆泄漏源,但我相当肯定这就是我的问题的原因。

    8 回复  |  直到 14 年前
        1
  •  5
  •   lpfavreau Arthur Debert    15 年前

    查看代码会有所帮助,但如果您想自己调试代码,请查看 Xdebug ,这将有助于分析您的应用程序。

    当然,这取决于你在做什么,它可能正在积累一些内存,尽管对于2500个记录来说132MB似乎已经很高了。当然,你可以 tweak your memory limit 在php.ini中,如果需要。

    您正在读取的csv文件有多大?你对它做了什么样的处理?

        2
  •  7
  •   too much php    15 年前

    如果您确实怀疑脚本中只有一个或两个内存泄漏导致脚本崩溃,那么您应该采取以下步骤:

    • 变化 memory_limit 对于一些小的东西,比如500kb
    • 注释掉除一个处理步骤以外的所有应用于每行的处理步骤。
    • 对整个csv文件运行有限的处理,看看是否可以完成。
    • 逐渐地增加更多的步骤,然后观察内存使用是否激增。

    例子:

    ini_set('memory_limit', 1024 * 500);
    $fp = fopen("test.csv", 'r');
    while($row = fgetcsv($fp)) {
        validate_row($row);         // step 1: validate
        // add these back in one by one and keep an eye on memory usage
        //calculate_fizz($row);     // step 2: fizz
        //calculate_buzz($row);     // step 3: buzz
        //triangulate($row);        // step 4: triangulate
    }
    echo "Memory used: ", memory_get_peak_usage(), "\n";
    

    最坏的情况是 全部的 您的处理步骤中有一些效率较低,您需要优化所有这些步骤。

        3
  •  2
  •   Vinko Vrsalovic    15 年前

    这取决于在处理完变量后如何清除它们。

    看起来您已经完成了记录,但仍在某处存储信息。使用 unset() 如果有疑问,清除变量。

    请提供一个最小的复制代码示例,以查看如果这不起作用,所有的内存将流向何处。

    顺便说一句,生成最小的代码样本来重现这个问题是一种很好的调试技术,因为它迫使您小心地重新检查代码。

        4
  •  2
  •   stefs    15 年前

    您可以尝试本地安装php5.3并调用http://www.php.net/manual/en/function.gc-collect-cycles.php。

    gc_collect_cycles 强制收集任何现有垃圾循环

    如果情况改善了,您至少验证了(关于)问题。

        5
  •  1
  •   UnkwnTech    15 年前

    你是怎么读文件的?如果您使用fread/filegetcontents或其他类似的函数,那么当调用时加载整个文件时,您将消耗内存中的整个文件大小(或者使用fread加载的量)。但是如果你使用 fgetcsv 如果一次只读一行,这取决于行的长度,这在你的记忆中会明显地更容易。

    还要确保在每个循环上重用尽可能多的变量。检查是否没有包含大量数据的数组。

    最后一点要注意的是,在循环之前打开文件,然后在单词后关闭:

    $fh = fopen(...);
    while(true)
    {
    //...
    }
    fclose($fh);
    

    你真的不想这样做:

    while(true)
    {
    $fh = fopen(...);
    //...
    fclose($fh);
    }
    

    就像其他人说的那样,如果没有看到一些代码,就很难分辨出来。

        6
  •  0
  •   Jani Hartikainen    15 年前

    在没有看到任何代码的情况下很难说出原因。但是,一个典型的问题是递归引用,即对象A指向对象B,而另一个方向指向对象B,这可能导致GC出错。

    我不知道您当前如何处理该文件,但您可以尝试一次只能读取一行文件。如果您一次读取整个文件,它可能会消耗更多的内存。

    这实际上是我喜欢批处理任务使用Python的原因之一。

        7
  •  0
  •   Community uzul    7 年前

    您能在php.ini中更改内存限制吗?

    此外,对变量执行unset($var)操作是否可以释放一些内存?$var=null也可以帮助吗?

    另请参见此问题: What's better at freeing memory with PHP: unset() or $var = null

        8
  •  0
  •   Simon Lang    14 年前

    我也遇到了同样的问题,这也是由于数据库分析(zend_db_profiler_firebug)。在我的情况下,它每分钟泄漏1兆字节。这个脚本应该运行几天,所以几个小时内就会崩溃。

    推荐文章