诊断内存泄漏 - 允许#bytes的内存大小耗尽

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

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

Increasing the limit

If you know what you're doing and want to increase the limit see 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:

foreach ($users as $user) {
    $task = new Task;
    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 $user or Task.

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

dsmvqp3124 附:-我最近遇到了这种确切类型的问题。不幸的是,我还发现php有一个子对象破坏问题。如果取消设置父对象,则不会释放其子对象。必须确保我使用修改后的unset,其中包括对所有子对象__destruct等的递归调用。详细信息:paul-m-jones.com/archives/262::我正在做类似的事情:functionsuper_unset($item){if(is_object($item)&&method_exists($item,“__destruct”)){$项->__自毁();}unset($item);}
10 年多之前 回复


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

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

  resources.db.profiler.enabled = true 
resources.db.profiler.class = Zend_Db_Profiler_Firebug
</ code> </ pre>

在此之前运行大约25.000个查询+加载处理,使内存达到了一个很好的128Mb(我的最大内存限制)。</ p>
\ n

只需设置:</ p>

  resources.db.profiler.enabled = false 
</ code> </ pre>

它是 足以让它低于20 Mb </ p>

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

通过 xDebug profiling </ p>
确实有助于运行脚本 </ DIV>



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

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:

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).

By just setting:

resources.db.profiler.enabled    = false

it was enough to keep it under 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.

It really helped running the script with xDebug profiling

在php中有几个可能的内存泄漏点:</ p>

  • php 本身</ li>
  • php扩展</ li>
  • 你使用的php库</ li>
  • 你的php代码</ li>
    </ ul>
    \ n

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



There are several possible points of memory leaking in php:

  • php itself
  • php extension
  • php library you use
  • your php code

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

doufei5315 令人遗憾的是,即使是php 7.2他们也无法修复核心的php内存泄漏。 您无法在其中运行长时间运行的进程。
2 年多之前 回复
douchun9719 并且明确......良好的起点。
大约 8 年之前 回复
doujiao2000 你的回答是关于它可能得到的一般性
11 年多之前 回复

我最近在一个应用程序上遇到了这个问题,在我收集到的类似情况下。 一个在PHP的cli中运行的脚本,它遍历许多迭代。 我的脚本依赖于几个底层库。 我怀疑某个特定的库是原因,我花了几个小时徒劳地试图在它的类中添加适当的析构方法无济于事。 面对一个漫长的转换过程到一个不同的库(可能会出现同样的问题),我想出了一个粗略的解决方案来处理我的问题。</ p>

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

  foreach($ ids as $ id){
$ lines = array();
exec(“ php ./path/to/my/classes.php $ id“,$ lines);
foreach($ lines as $ line){echo $ line。”
“; } //显示一些输出
</ code> </ pre>

显然这种方法有局限性,需要注意这种危险,因为它很容易 创造一个兔子的工作,但在一些罕见的情况下,它可能有助于克服困难,直到找到更好的解决方案,如我的情况。</ p>
</ div>



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.

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:

foreach ($ids as $id) {
   exec("php ./path/to/my/classes.php $id", $lines);
   foreach ($lines as $line) { echo $line."
"; } //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.

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

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

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

doutang7414 感谢更新! 你是对的,这不再是一个问题所以我不应该害怕现在疯狂地使用它们。
8 年多之前 回复
dtcmadj31951 我会说同样的话。 这已于5.3.10(#60139)修订
8 年多之前 回复

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:

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
", 3, '/var/log/httpd/php_memory_log');

Employ it by adding the following to 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

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

我建议你查看php手册或添加 gc_enable()</ code>函数来收集垃圾.. 那就是内存泄漏不会影响代码的运行方式。</ p>

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

</ div>



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.

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

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



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_usage and narrow it down to where the code leaks. You can also use xdebug to create a trace of the code. Run the code with execution traces and show_mem_delta.

dongzhuang5741 说得通。 谢谢你回到我身旁。
接近 4 年之前 回复
dongwen4630 - 已经有一段时间了(正如你从时间戳中看到的那样),所以我真的不记得了......但是,我认为我监控了代码的内存使用情况,以验证是否发生了泄漏。 然后,我做了一个更改(例如添加unset())和测试。 一旦我发现了对内存泄漏有影响的东西,我就知道我在取得进步! 希望这有帮助! :)
接近 4 年之前 回复
duanmei1885 那么你是怎么发现泄漏的? 你能分享一下你采取的步骤吗?
接近 4 年之前 回复
dongtui4038 +1发现泄漏! 一个有循环引用的类! 一旦这些引用被取消(),对象就像预期的那样被垃圾收集了! 谢谢! :)
大约 7 年之前 回复
dousonghs58612 由于5.3 PHP实际上有一个垃圾收集器。 另一方面,内存分析功能已被删除xdebug :(
接近 9 年之前 回复
duanchun1909 是的,它非常沉重.. GB听起来有点多 - 除非你有一个大脚本。 也许尝试只处理几行(应该足以识别泄漏)。 另外,请勿在生产服务器上安装xdebug扩展。
11 年多之前 回复
dsajdgjadwqe3247382 但请注意......生成的跟踪文件将是ENORMOUS。 我第一次在Zend Framework应用程序上运行xdebug跟踪时,需要花费很长时间来运行并生成一个多GB(不是kb或MB ... GB)大小的文件。 请注意这一点。
11 年多之前 回复

如果您对PHP的说法仅在函数为真后执行GC,则可以将循环的内容包装在函数内作为变通方法/ 实验。</ p>
</ div>



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.

douchuitang0331 其实我认为我的回答是错误的。 毕竟,调用的run()也是一个函数,最后应该发生GC。
8 年多之前 回复
dongru2019 非常聪明,我喜欢它
8 年多之前 回复

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($users as $user){
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.

douluo1330 这与内存泄漏无关。 这只是数组指针的变化。 看看这里:prismnet.com/~mcmahon/Notes/arrays_and_pointers.html版本3a。
一年多之前 回复
dongxia19772008 你可以取消它()它,但要记住,对于对象,你所做的只是改变你的变量指向的位置 - 你实际上没有从内存中删除它。 一旦它超出范围,PHP将自动释放内存,所以更好的解决方案(就这个答案而言,而不是OP的问题)是使用短函数,因此他们也不会从循环中挂起该变量 长。
接近 2 年之前 回复
dongyu1125 我认为这是设计的。 它的一个好处是,在循环之后,您可以使用您找到的最后一个项目,例如满足特定条件。
大约 9 年之前 回复
dongzhonggua4229 PHP没有范围像C / Java /等的循环/条件。 即使在退出循环/条件(通过设计[?])之后,在循环/条件内声明的任何内容仍然在范围内。 另一方面,方法/函数按照您的预期进行范围化 - 一旦函数执行结束,所有内容都会被释放。
9 年多之前 回复

我没有看到它明确提到,但 xdebug 可以很好地分析时间和记忆</ strong>(截至 2.6 )。 您可以获取它生成的信息并将其传递给您选择的gui前端: webgrind ( 仅限时间), kcachegrind qcachegrind 或其他人,它会生成非常有用的调用树和图形,以便您找到各种困境的来源。</ p>

示例(qcachegrind) ):
</ p>
</ div>



I didn't see it explicitly mentioned, but xdebug does a great job profiling time and memory (as of 2.6). You can take the information it generates and pass it off to a gui front end of your choice: webgrind (time only), kcachegrind, qcachegrind or others and it generates very useful call trees and graphs to let you find the sources of your various woes.

Example (of qcachegrind): enter image description here

共13条数据 1 尾页
Csdn user default icon