如何修复 PHP 中的内存泄漏

发布于 2024-07-24 15:51:13 字数 519 浏览 3 评论 0原文

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

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

导入大约 2500 条记录后,PHP 停止运行,并表示已超出内存限制(132 MB 左右)。

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

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

发现问题的原因

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

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

My PHP app has an import script that can import records.

At the moment, it is importing from a CSV file. It is reading each line of the CSV file, one line at a time using fgetcsv, and for each line it is doing a lot of processing on that record, including database queries, and then moving on to the next line. It shouldn't need to keep accumulating more memory.

After around 2500 records imported, PHP dies, saying that it has run over its memory limit (132 MB or so).

The CSV file itself is only a couple of megs - the other processing that happens does a lot of string comparisons, diffs, etc. I have a huge amount of code operating on it and it would be difficult to come up with a 'smallest reproducing sample'.

What are some good ways to go about finding and fixing such a problem?

Cause of problem found

I have a debug class which logs all my database queries during runtime. So those strings of SQL, some 30KB long, were staying in memory. I realise this isn't suitable for scripts designed to run for a long time.

There may be other sources of memory leaks, but I am fairly sure this is the cause of my problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

荒路情人 2024-07-31 15:51:13

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

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

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

If you do in fact suspect that there are just one or two memory leaks in your script which are causing it to crash, then you should take the following steps:

  • Change memory_limit to something small, like 500KB
  • Comment out all but one of the processing steps which is applied to each row.
  • Run the limited processing over the whole CSV file and see if it can complete.
  • Gradually add more steps back in and watch to see if memory usage spikes.

Example:

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

The worst case scenario is that all of your processing steps are moderately inefficient and you will need to optimize all of them.

无法回应 2024-07-31 15:51:13

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

当然,根据您在做什么,它可能会积累一些内存,尽管 132MB 对于 2500 条记录来说似乎已经很高了。 当然,您可以调整内存限制如果需要的话,在 php.ini 中。

您正在读取的 CSV 文件有多大? 您正在对其进行哪些对象和类型的处理?

It would help to have a look at the code but if you want to debug it yourself, have a look at Xdebug, it'll help profile your application.

Of course, depending on what you are doing, it is possible it's accumulating some memory, although 132MB seems already high for 2500 records. Of course, you can tweak your memory limit in php.ini if needed.

How big is the CSV file you are reading? And what objects and kind of processing are you doing to it?

很快妥协 2024-07-31 15:51:13

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

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

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

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

It depends on how are you clearing the variables after being done with them.

It looks like you are done with the record but you are still storing the information somewhere. Use unset() to clear variables up if in doubt.

Please provide a minimal reproducing code sample to see where is all that memory going if this doesn't help.

BTW, producing the smallest code sample that will reproduce the problem is a great debugging technique because it forces you to go through the code again, with care.

离线来电— 2024-07-31 15:51:13

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

gc_collect_cycles — 强制收集任何现有的垃圾周期

,您至少验证了问题。

you could try a local installation of php5.3 and call http://www.php.net/manual/en/function.gc-collect-cycles.php.

gc_collect_cycles — Forces collection of any existing garbage cycles

if the situation improves, you at least verified (on of) the problem(s).

海风掠过北极光 2024-07-31 15:51:13

您如何阅读该文件? 如果您使用 fread/filegetcontents 或其他此类函数,那么当整个文件在调用时加载时,您将消耗内存中的整个文件大小(或使用 fread 加载的大小)。 但是,如果您使用 fgetcsv 如果一次只会读取一行,具体取决于行的长度,这可以是您的记忆力会大大提高。

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

最后一点,还要确保您在循环之前打开文件,然后在事后关闭它:

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

您真的不想这样做:

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

就像其他人所说的那样,如果没有看到一些代码,就很难判断。

How are you reading the file? If your using fread/filegetcontents or other such functions then you are going to consume the entire file size (or however much you load with fread) in memory as the entire file is loaded at call time. However if you use fgetcsv if will only read one line at a time depending on the length of the line this can be dramaticly easier on your memory.

Also make sure that you are reusing as many variables as possible on each loop. Check that there are no array with large amounts of data in them.

As a last note also make sure that you are opening your file before your loop then closing it afterwords:

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

You don't realy want to be doing this:

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

And like others have said it'll be hard to tell without seeing some code.

明明#如月 2024-07-31 15:51:13

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

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

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

It's difficult to say the cause without seeing any code. However, a typical issue is recursive references, ie. object A points to object B and the other way around, which may cause the GC to screw up.

I don't know how you're currently processing the file, but you could attempt to only read the file one row at a time. If you read the whole file at once it may consume more memory.

This is actually one of the reasons I often prefer Python for batch processing tasks.

初熏 2024-07-31 15:51:13

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

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

另请参阅此问题:释放内存效果更好对于 PHP:unset() 或 $var = null

Are you able to change your memory_limit in your php.ini?

Also, could doing unset($var) on variables free up some memory? Could $var = null help too?

See also this question: What's better at freeing memory with PHP: unset() or $var = null

沦落红尘 2024-07-31 15:51:13

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

I was having the same problem, and it was also due to database profiling (Zend_Db_Profiler_Firebug). In my case it was leaking 1mb per minute. this script was supposed to run for days, so it would crash within a few hours.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文