允许的#字节内存大小已耗尽
我遇到了可怕的错误消息,可能是经过艰苦的努力,PHP已经耗尽内存:
在第123行的file.php中允许####字节的内存大小已耗尽(试图分配####字节)
增加限制
如果你知道你在做什么,并想增加限制,请参阅memory_limit:
ini_set('memory_limit', '16M');
ini_set('memory_limit', -1); // no 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
}
出于这个问题的目的,我们假设可以想象的最糟糕的意大利面代码隐藏在全局范围的$user
或Task
。
什么工具,PHP技巧或调试巫术可以帮助我找到并解决问题?
PHP没有垃圾收集器。 它使用引用计数来管理内存。 因此,最常见的内存泄漏来源是循环引用和全局变量。 如果你使用一个框架,恐怕会有很多代码来寻找它。 最简单的工具是有选择地调用memory_get_usage
并将其缩小到代码泄漏的位置。 您也可以使用xdebug创建代码的跟踪。 使用执行跟踪和show_mem_delta
运行代码。
在php中有几种可能的内存泄漏点:
如果没有深度逆向工程或php源代码知识,很难找到并修复前3个。 对于最后一个,你可以使用memory_get_usage对二进制搜索内存泄漏代码
这是我们用来确定哪些脚本在我们的服务器上使用最多内存的技巧。
将以下片段保存在文件中,例如/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 $urln", 3, '/var/log/httpd/php_memory_log');
}
register_shutdown_function('strangecode_log_memory_usage');
通过将以下内容添加到httpd.conf中来应用它:
php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php
然后分析/var/log/httpd/php_memory_log
的日志文件
在Web用户可以写入日志文件之前,您可能需要先touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log
。