我的PHP应用程序有一个可以导入记录的导入脚本。
目前,它正在从CSV文件导入。它正在读取CSV文件的每一行,使用fgetcsv一次一行,并且对于每一行,它正在对该记录执行很多处理,包括数据库查询,然后继续下一行。它不应该继续累积更多的内存。
在导入大约2500条记录后,PHP死了,说已经超过了内存限制(132 MB左右)。
CSV文件本身只有几个meg - 发生的其他处理会进行大量的字符串比较,差异等。我有大量的代码在运行它并且很难想出一个'最小的复制样本'。
找到并解决这个问题有哪些好方法?
发现问题的原因
我有一个调试类,它在运行时记录我的所有数据库查询。因此,那些长约30KB的SQL字符串留在内存中。我意识到这不适合设计为长时间运行的脚本。
可能还有其他内存泄漏源,但我很确定这是导致我出现问题的原因。
答案 0 :(得分:7)
如果您确实怀疑脚本中只有一两个内存泄漏导致崩溃,那么您应该采取以下步骤:
memory_limit
更改为小的内容,例如500KB 示例:
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。这个脚本应该运行几天,所以它会在几个小时内崩溃。