如何着手修复PHP中的内存泄漏

如何着手修复PHP中的内存泄漏,php,memory-leaks,reference-counting,Php,Memory Leaks,Reference Counting,我的PHP应用程序有一个可以导入记录的导入脚本 目前,它是从CSV文件导入的。它读取CSV文件的每一行,使用fgetcsv一次读取一行,并对每一行记录进行大量处理,包括数据库查询,然后转到下一行。它不需要不断积累更多的内存 在导入了大约2500条记录之后,PHP死掉了,说它已经超过了内存限制(132MB左右) CSV文件本身只有几个MEG——另一个处理过程会进行大量的字符串比较、差异等。我有大量的代码对其进行操作,很难找到“最小的复制样本” 找到并解决此类问题的好方法有哪些 发现问题的原因 我有

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

目前,它是从CSV文件导入的。它读取CSV文件的每一行,使用fgetcsv一次读取一行,并对每一行记录进行大量处理,包括数据库查询,然后转到下一行。它不需要不断积累更多的内存

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

CSV文件本身只有几个MEG——另一个处理过程会进行大量的字符串比较、差异等。我有大量的代码对其进行操作,很难找到“最小的复制样本”

找到并解决此类问题的好方法有哪些

发现问题的原因

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


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

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

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


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

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

看起来您已处理完该记录,但仍将信息存储在某个位置。如果有疑问,使用清除变量

请提供一个最小的复制代码示例,看看如果这样做没有帮助的话,所有的内存都到哪里去了


顺便说一句,生成能够重现问题的最小代码示例是一种很好的调试技术,因为它迫使您重新仔细检查代码。

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

当然,根据您正在做的事情,它可能正在积累一些内存,尽管对于2500条记录来说132MB似乎已经很高了。当然,如果需要,可以在php.ini中使用


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

您如何读取文件?如果您使用fread/filegetcontents或其他类似函数,那么在调用时加载整个文件时,您将消耗内存中的整个文件大小(或使用fread加载的大小)。然而,如果你使用if,根据行的长度,一次只能读一行,这会大大简化你的记忆

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

最后,请确保在循环前打开文件,然后在循环结束后关闭:

$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);
你真的不想这样做:

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

正如其他人所说,如果没有看到一些代码,很难判断。您是否能够更改php.ini中的内存限制

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


另请参见此问题:

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

  • memory\u 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";

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

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

gc\u collect\u cycles
-强制收集任何现有垃圾周期


如果情况有所改善,您至少验证了问题。

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

它一次读取一行CSV。整个CSV只有几个兆欧;问题似乎出在其他处理上。它们不是被称为循环引用吗?@Schnalle完全正确。我太困了,记不起正确的术语=)PHP5.3中的垃圾收集器应该处理循环引用。在旧的PHP版本中,这确实是一个问题。+1我在PHP中经常使用CSV文件,最终会遇到这个问题。我在CodeIgniter处理大型日志文件并插入数据库时遇到过类似的问题。我将插入更改为使用CodeIgniters
simple_query
而不是通常的
query
方法,并将内存使用减少了10倍。感谢xdebug的建议。我现在找到了原因:)谢谢你的建议。对于任何有类似问题的人来说,这是一个很好的建议,但我碰巧发现xdebug的“跟踪”功能在查找原因方面最为有用。这是一个很好的建议,但内存限制已经达到128MB左右,增加它只会让它运行更长一点——最终我希望能够进口超过10个,也许50个钛