php 诊断内存泄漏 - 已用完 # 字节的允许内存大小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/849549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 00:05:08  来源:igfitidea点击:

Diagnosing Memory Leaks - Allowed memory size of # bytes exhausted

phpmemory-leaks

提问by Mike B

I've encountered the dreaded error-message, possibly through-painstaking effort, PHP has run out of memory:

我遇到了可怕的错误消息,可能是经过艰苦的努力,PHP 内存不足:

Allowed memory size of #### bytes exhausted (tried to allocate #### bytes) in file.php on line 123

在第 123 行的 file.php 中允许的内存大小为 #### 字节耗尽(尝试分配 #### 字节)

Increasing the limit

增加限额

If you know what you're doing and want to increase the limit see memory_limit:

如果您知道自己在做什么并想增加限制,请参阅memory_limit

ini_set('memory_limit', '16M');
ini_set('memory_limit', -1); // no limit

Beware! You may only be solving the symptom and not the problem!

谨防!您可能只是在解决症状而不是问题!

Diagnosing the leak:

泄漏诊断:

The error message points to a line withing a loop that I believe to be leaking, or needlessly-accumulating, memory. I've printed memory_get_usage()statements at the end of each iteration and can see the number slowly grow until it reaches the limit:

错误消息指向带有循环的一行,我认为该行正在泄漏或不必要地累积内存。我memory_get_usage()在每次迭代结束时打印语句,可以看到数字缓慢增长,直到达到限制:

foreach ($users as $user) {
    $task = new Task;
    $task->run($user);
    unset($task); // Free the variable in an attempt to recover memory
    print memory_get_usage(true); // increases over time
}

For the purposes of this question let's assume the worst spaghetti code imaginable is hiding in global-scope somewhere in $useror Task.

出于这个问题的目的,让我们假设可以想象到的最糟糕的意大利面条式代码隐藏在$user或中的全局范围内Task

What tools, PHP tricks, or debugging voodoo can help me find and fix the problem?

哪些工具、PHP 技巧或调试伏都教可以帮助我找到并解决问题?

回答by troelskn

PHP doesn't have a garbage collector. It uses reference counting to manage memory. Thus, the most common source of memory leaks are cyclic references and global variables. If you use a framework, you'll have a lot of code to trawl through to find it, I'm afraid. The simplest instrument is to selectively place calls to memory_get_usageand narrow it down to where the code leaks. You can also use xdebugto create a trace of the code. Run the code with execution tracesand show_mem_delta.

PHP 没有垃圾收集器。它使用引用计数来管理内存。因此,最常见的内存泄漏源是循环引用和全局变量。如果您使用一个框架,恐怕您将有很多代码需要搜索才能找到它。最简单的工具是有选择地调用memory_get_usage并将其缩小到代码泄漏的地方。您还可以使用xdebug来创建代码跟踪。使用执行跟踪show_mem_delta.

回答by Quinn Comendant

Here's a trick we've used to identify which scripts are using the most memory on our server.

这是我们用来确定哪些脚本在我们的服务器上使用最多内存的技巧。

Save the following snippet in a file at, e.g., /usr/local/lib/php/strangecode_log_memory_usage.inc.php:

将以下代码段保存在文件中,例如/usr/local/lib/php/strangecode_log_memory_usage.inc.php

<?php
function strangecode_log_memory_usage()
{
    $site = '' == getenv('SERVER_NAME') ? getenv('SCRIPT_FILENAME') : getenv('SERVER_NAME');
    $url = $_SERVER['PHP_SELF'];
    $current = memory_get_usage();
    $peak = memory_get_peak_usage();
    error_log("$site current: $current peak: $peak $url\n", 3, '/var/log/httpd/php_memory_log');
}
register_shutdown_function('strangecode_log_memory_usage');

Employ it by adding the following to httpd.conf:

通过将以下内容添加到 httpd.conf 来使用它:

php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php

Then analyze the log file at /var/log/httpd/php_memory_log

然后分析日志文件在 /var/log/httpd/php_memory_log

You might need to touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_logbefore your web user can write to the log file.

touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log在您的 Web 用户写入日志文件之前,您可能需要这样做。

回答by patcoll

I noticed one time in an old script that PHP would maintain the "as" variable as in scope even after my foreach loop. For example,

我曾经在一个旧脚本中注意到,即使在我的 foreach 循环之后,PHP 也会将“as”变量保持在范围内。例如,

foreach($users as $user){
  $user->doSomething();
}
var_dump($user); // would output the data from the last $user 

I'm not sure if future PHP versions fixed this or not since I've seen it. If this is the case, you could unset($user)after the doSomething()line to clear it from memory. YMMV.

我不确定未来的 PHP 版本是否修复了这个问题,因为我已经看过了。如果是这种情况,您可以unset($user)在该doSomething()行之后将其从内存中清除。天啊。

回答by kingoleg

There are several possible points of memory leaking in php:

php内存泄漏有几个可能的点:

  • php itself
  • php extension
  • php library you use
  • your php code
  • php本身
  • 扩展名
  • 你使用的php库
  • 你的 php 代码

It is quite hard to find and fix the first 3 without deep reverse engineering or php source code knowledge. For the last one you can use binary search for memory leaking code with memory_get_usage

如果没有深入的逆向工程或 php 源代码知识,很难找到并修复前 3 个。对于最后一个,您可以使用memory_get_usage使用二进制搜索查找内存泄漏代码

回答by Nate Flink

I recently ran into this problem on an application, under what I gather to be similar circumstances. A script that runs in PHP's cli that loops over many iterations. My script depends on several underlying libraries. I suspect a particular library is the cause and I spent several hours in vain trying to add appropriate destruct methods to it's classes to no avail. Faced with a lengthy conversion process to a different library (which could turn out to have the same problems) I came up with a crude work around for the problem in my case.

我最近在一个应用程序上遇到了这个问题,在我认为是类似的情况下。在 PHP 的 cli 中运行的脚本,该脚本循环多次迭代。我的脚本依赖于几个底层库。我怀疑是一个特定的库是原因,我花了几个小时试图向它的类添加适当的破坏方法但徒劳无功。面对到不同库的漫长转换过程(结果可能会出现相同的问题),我想出了一个粗略的解决方法来解决我的问题。

In my situation, on a linux cli, I was looping over a bunch of user records and for each one of them creating a new instance of several classes I created. I decided to try creating the new instances of the classes using PHP's exec method so that those process would run in a "new thread". Here is a really basic sample of what I am referring to:

在我的情况下,在 linux cli 上,我正在遍历一堆用户记录,并为每个记录创建我创建的几个类的新实例。我决定尝试使用 PHP 的 exec 方法创建类的新实例,以便这些进程在“新线程”中运行。这是我所指的一个非常基本的示例:

foreach ($ids as $id) {
   $lines=array();
   exec("php ./path/to/my/classes.php $id", $lines);
   foreach ($lines as $line) { echo $line."\n"; } //display some output
}

Obviously this approach has limitations, and one needs to be aware of the dangers of this, as it would be easy to create a rabbit job, however in some rare cases it might help get over a tough spot, until a better fix could be found, as in my case.

显然这种方法有局限性,人们需要意识到这样做的危险,因为它很容易创建一个兔子工作,但是在某些极少数情况下,它可能有助于克服困难,直到找到更好的解决方案,就像我的情况一样。

回答by Gunnar Lium

I came across the same problem, and my solution was to replace foreach with a regular for. I'm not sure about the specifics, but it seems like foreach creates a copy (or somehow a new reference) to the object. Using a regular for loop, you access the item directly.

我遇到了同样的问题,我的解决方案是用常规 for 替换 foreach。我不确定具体细节,但似乎 foreach 为该对象创建了一个副本(或以某种方式创建了一个新引用)。使用常规 for 循环,您可以直接访问该项目。

回答by Kosgei

I would suggest you check the php manual or add the gc_enable()function to collect the garbage... That is the memory leaks dont affect how your code runs.

我建议您查看 php 手册或添加gc_enable()收集垃圾的功能...那是内存泄漏不会影响您的代码运行方式。

PS: php has a garbage collector gc_enable()that takes no arguments.

PS:php有一个gc_enable()不带参数的垃圾收集器。

回答by Xeoncross

I recently noticed that PHP 5.3 lambda functions leave extra memory used when they are removed.

我最近注意到 PHP 5.3 lambda 函数在删除时会留下额外的内存。

for ($i = 0; $i < 1000; $i++)
{
    //$log = new Log;
    $log = function() { return new Log; };
    //unset($log);
}

I'm not sure why, but it seems to take an extra 250 bytes each lambda even after the function is removed.

我不知道为什么,但即使在删除函数之后,每个 lambda 似乎也需要额外的 250 字节。

回答by Bart van Heukelom

If what you say about PHP only doing GC after a function is true, you could wrap the loop's contents inside a function as a workaround/experiment.

如果您所说的关于 PHP 仅在函数之后执行 GC 的说法为真,您可以将循环的内容包装在函数中作为解决方法/实验。

回答by Andy

One huge problem I had was by using create_function. Like in lambda functions, it leaves the generated temporary name in memory.

我遇到的一个大问题是使用create_function。就像在 lambda 函数中一样,它将生成的临时名称留在内存中。

Another cause of memory leaks (in case of Zend Framework) is the Zend_Db_Profiler. Make sure that is disabled if you run scripts under Zend Framework. For example I had in my application.ini the folowing:

内存泄漏的另一个原因(在 Zend Framework 的情况下)是 Zend_Db_Profiler。如果您在 Zend Framework 下运行脚本,请确保已禁用。例如,我在 application.ini 中有以下内容:

resources.db.profiler.enabled    = true
resources.db.profiler.class      = Zend_Db_Profiler_Firebug

Running approximately 25.000 queries + loads of processing before that, brought the memory to a nice 128Mb (My max memory limit).

在此之前运行大约 25.000 个查询 + 处理负载,使内存达到了 128Mb(我的最大内存限制)。

By just setting:

只需设置:

resources.db.profiler.enabled    = false

it was enough to keep it under 20 Mb

将其保持在 20 Mb 以下就足够了

And this script was running in CLI, but it was instantiating the Zend_Application and running the Bootstrap, so it used the "development" config.

这个脚本在 CLI 中运行,但它正在实例化 Zend_Application 并运行 Bootstrap,所以它使用了“开发”配置。

It really helped running the script with xDebug profiling

它确实有助于使用xDebug 分析运行脚本