Php 诊断内存泄漏-允许的内存大小为#字节

Php 诊断内存泄漏-允许的内存大小为#字节,php,memory-leaks,Php,Memory Leaks,我遇到了可怕的错误消息,可能是经过艰苦的努力,PHP内存不足: 第123行file.php中允许的内存大小为########字节(尝试分配#####字节) 提高限额 如果您知道自己在做什么,并希望增加限额,请参见: 当心!你可能只是在解决症状,而不是问题 诊断泄漏: 错误消息指向一条带循环的线,我认为该线正在泄漏或不必要地积累内存。我已经在每次迭代结束时打印了memory\u get\u usage()语句,可以看到该数字慢慢增长,直到达到极限: foreach ($users as $user

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

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

提高限额 如果您知道自己在做什么,并希望增加限额,请参见:

当心!你可能只是在解决症状,而不是问题

诊断泄漏: 错误消息指向一条带循环的线,我认为该线正在泄漏或不必要地积累内存。我已经在每次迭代结束时打印了
memory\u get\u 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
}
出于这个问题的目的,让我们假设可以想象的最糟糕的意大利面代码隐藏在
$user
Task
的全局范围内


什么工具、PHP技巧或调试voodoo可以帮助我找到并解决问题?

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

  • php本身
  • php扩展
  • 您使用的php库
  • 您的php代码

如果没有深入的逆向工程或php源代码知识,很难找到并修复前3个。对于最后一个,您可以使用二进制搜索查找内存泄漏代码,PHP没有垃圾收集器。它使用引用计数来管理内存。因此,最常见的内存泄漏源是循环引用和全局变量。如果你使用一个框架,恐怕你会有很多代码需要搜索才能找到它。最简单的方法是有选择地调用
内存\u get\u usage
,并将其缩小到代码泄漏的地方。您还可以使用创建代码的跟踪。使用和运行代码
show_mem_delta

如果您所说的PHP只在函数后执行GC是真的,那么您可以将循环的内容包装到函数中作为一种变通方法/实验。

我在一个旧脚本中注意到,即使在我的foreach循环之后,PHP也会将“as”变量保持在作用域中。比如说,

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

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

我最近在一个应用程序中遇到了这个问题,据我推测,情况与此类似。在PHP的cli中运行的脚本,可循环多次迭代。我的脚本依赖于几个底层库。我怀疑是某个特定的库造成的,我花了几个小时试图向它的类中添加适当的析构函数方法,但徒劳无功。面对到另一个库的漫长转换过程(可能会出现同样的问题),我想出了一个粗略的解决方案

在我的情况下,在LinuxCLI上,我在一堆用户记录上循环,并为每个记录创建我创建的几个类的新实例。我决定尝试使用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
}

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

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

我最近注意到PHP5.3 lambda函数在删除时会留下额外的内存

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

{
//$log=新日志;
$log=函数(){返回新日志;};
//未结算($log);
}

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

我建议您查看php手册或添加
gc\u enable()
函数来收集垃圾。。。也就是说,内存泄漏不会影响代码的运行方式


PS:php有一个垃圾收集器
gc_enable()
,它不带任何参数。

我有点晚了,但我将分享一些与Zend Framework相关的内容


在安装PHP5.3.8(使用phpfarm)以使用PHP5.2.9开发的ZF应用程序后,我遇到了内存泄漏问题。我发现内存泄漏是在Apache的httpd.conf文件中触发的,在我的虚拟主机定义中,它说
SetEnv APPLICATION\u ENV“development”
。在注释掉这一行之后,内存泄漏停止了。我试图在我的php脚本中找到一个内联解决方案(主要是通过在main index.php文件中手动定义它)。

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

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

resources.db.profiler.enabled    = true
resources.db.profiler.class      = Zend_Db_Profiler_Firebug
在此之前,运行大约25000个查询加上大量处理,使内存达到了128Mb(我的最大内存限制)

只需设置:

resources.db.profiler.enabled    = false
这足以使它保持在20MB以下

这个脚本在CLI中运行,但是它实例化了Zend_应用程序并运行引导程序,所以它使用了“development”配置


它确实有助于使用

运行脚本。我们使用了一个技巧来确定哪些脚本使用了服务器上最多的内存

将以下代码段保存在文件中,例如,
/usr/local/lib/php/squaredcode\u log\u memory\u 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');
然后肛门
<?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');
php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php