如何在PHP中修复内存泄漏

时间:2009-06-18 01:53:31

标签: php memory-leaks reference-counting

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

目前,它正在从CSV文件导入。它正在读取CSV文件的每一行,使用fgetcsv一次一行,并且对于每一行,它正在对该记录执行很多处理,包括数据库查询,然后继续下一行。它不应该继续累积更多的内存。

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

CSV文件本身只有几个meg - 发生的其他处理会进行大量的字符串比较,差异等。我有大量的代码在运行它并且很难想出一个'最小的复制样本'。

找到并解决这个问题有哪些好方法?

发现问题的原因

我有一个调试类,它在运行时记录我的所有数据库查询。因此,那些长约30KB的SQL字符串留在内存中。我意识到这不适合设计为长时间运行的脚本。

可能还有其他内存泄漏源,但我很确定这是导致我出现问题的原因。

8 个答案:

答案 0 :(得分:7)

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

  • 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";

最糟糕的情况是,所有处理步骤的效率都是中等效率的,您需要优化所有这些步骤。

答案 1 :(得分:5)

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

当然,根据你正在做的事情,它可能会积累一些内存,尽管132MB似乎已经高达2500条记录。当然,如果需要,你可以在php.ini中tweak your memory limit

您正在阅读的CSV文件有多大?你正在做什么对象和处理?

答案 2 :(得分:2)

这取决于你在完成变量后如何清除变量。

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

请提供一个最小的再现代码示例,以查看如果这没有帮助,那么内存将会在哪里发生。

BTW,生成可以重现问题的最小代码示例是一种很好的调试技术,因为它会强制您再次仔细检查代码。

答案 3 :(得分:2)

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

  

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

如果情况有所改善,您至少可以验证(问题)。

答案 4 :(得分:1)

你是如何阅读文件的?如果你使用fread / filegetcontents或其他类似的函数,那么你将在内存中消耗整个文件大小(或者你用fread加载的多少),因为整个文件是在调用时加载的。但是,如果您使用fgetcsv,如果一次只读取一行,具体取决于行的长度,这可能会大大简化您的记忆。

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

作为最后一个注释,还要确保在循环之前打开文件,然后在之后关闭它:

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

你真的不想这样做:

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

和其他人一样,如果不看一些代码就很难说。

答案 5 :(得分:0)

如果没有看到任何代码,很难说出原因。但是,典型的问题是递归引用,即。对象A指向对象B,反之亦然,这可能导致GC搞砸。

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

这实际上是我经常喜欢Python进行批处理任务的原因之一。

答案 6 :(得分:0)

你能在php.ini中更改你的memory_limit吗?

另外,可以对变量进行unset($ var)释放一些内存吗? $ var = null也可以帮忙吗?

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

答案 7 :(得分:0)

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