我有一堆客户销售点 (POS) 系统,它们定期将新的销售数据发送到一个中央数据库,该数据库将数据存储到一个大数据库中以生成报告。
客户端 POS 基于 PHPPOS,并且我已经实现了一个使用标准 XML-RPC 库将销售数据发送到服务的模块。服务器系统建立在 CodeIgniter 之上,并使用 XML-RPC 和 XML-RPCS 库作为 Web 服务组件。每当我发送大量销售数据(销售表中少至 50 行,以及与销售中的每个项目相关的 sales_items 中的各个行)时,我都会收到以下错误:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 54 bytes)
128M 是 php.ini
中的默认值,但我认为这是一个很大的数字。事实上,我什至尝试过将此值设置为 1024M,但它所做的只是需要更长的时间才能出错。
至于我已采取的步骤,我尝试禁用服务器端的所有处理,并对其进行操纵以返回预设响应,而不管输入如何。但是,我认为问题在于数据的实际发送。我什至尝试禁用 PHP 的最大脚本执行时间,但它仍然出错。
ini_set('memory_limit', '256M');
将 memory_limit
更改为 ini_set('memory_limit', '-1');
是不是合适的解决方案。请不要那样做。
您的 PHP 代码可能在某处存在内存泄漏,而您正在告诉服务器只使用它想要的所有内存。你根本不会解决问题。如果您监视您的服务器,您会发现它现在可能正在使用大部分 RAM,甚至交换到磁盘。
您可能应该尝试追踪代码中的违规代码并修复它。
ini_set('memory_limit', '-1');
覆盖默认的 PHP memory limit。
-1
是 PHP 在此上下文中理解为 unlimited 的值。
正确的方法是编辑您的 php.ini
文件。将 memory_limit
编辑为您想要的值。
根据您的问题,已超出 128M
(这是默认限制),因此您的代码存在严重错误,因为它不应该占用那么多。
如果您知道为什么需要这么多,并且您希望将其设置为 memory_limit = 512M
或更高,那么您应该很好。
PHP 的内存分配可以永久或临时调整。
永久
您可以通过两种方式永久更改 PHP 内存分配。
如果您有权访问您的 php.ini
文件,则可以将 memory_limit
的值编辑为您想要的值。
如果您无权访问您的 php.ini
文件(并且您的网络主机允许),您可以通过您的 .htaccess
文件覆盖内存分配。添加 php_value memory_limit 128M
(或任何您想要的分配)。
暂时的
您可以在 PHP 文件中动态调整内存分配。您只需拥有代码 ini_set('memory_limit', '128M');
(或任何您想要的分配)。您可以通过将值设置为“-1”来移除内存限制(尽管机器或实例限制可能仍然适用)。
php -d memory_limit=256M your_php_file.php
或 php -d memory_limit=256M artisan ...
在 PHP 脚本中很容易出现内存泄漏 - 特别是如果您使用抽象,例如 ORM。尝试使用 Xdebug 分析您的脚本并找出所有内存的去向。
当使用 array_push 将 2250 万条记录添加到数组中时,我使用 4G
作为文件 php.ini 中的内存限制,在大约 20M 条记录处不断出现“内存耗尽”致命错误。为了解决这个问题,我添加了语句
$old = ini_set('memory_limit', '8192M');
在文件的顶部。现在一切正常。我不知道 PHP 是否有内存泄漏。那不是我的工作,我也不在乎。我只需要完成我的工作,这很奏效。
该程序非常简单:
$fh = fopen($myfile);
while (!feof($fh)) {
array_push($file, stripslashes(fgets($fh)));
}
fclose($fh);
致命错误指向第 3 行,直到我提高了内存限制,从而消除了错误。
ini_set('memory_limit', '8192M');
?
即使在 php.ini
中设置了 memory_limit
,并且使用 phpinfo()
正确读取的值,我也不断收到此错误。
通过改变它:
memory_limit=4G
对此:
memory_limit=4096M
这纠正了 PHP 7 中的问题。
当您看到上述错误时 - 特别是如果 (tried to allocate __ bytes)
是一个低值,这可能是一个无限循环的指标,就像一个函数调用自己没有出路:
function exhaustYourBytes()
{
return exhaustYourBytes();
}
您可以通过更改 fastcgi/fpm 上的 memory_limit
来正确解决此问题:
$vim /etc/php5/fpm/php.ini
更改内存,例如从 128 到 512,见下文
; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 128M
至
; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 512M
您网站的根目录:
ini_set('memory_limit', '1024M');
启用这两行后,它开始工作:
; Determines the size of the realpath cache to be used by PHP. This value should
; be increased on systems where PHP opens many files to reflect the quantity of
; the file operations performed.
; http://php.net/realpath-cache-size
realpath_cache_size = 16k
; Duration of time, in seconds for which to cache realpath information for a given
; file or directory. For systems with rarely changing files, consider increasing this
; value.
; http://php.net/realpath-cache-ttl
realpath_cache_ttl = 120
https://content.screencast.com/users/PKMauyra/folders/Jing/media/8afa91fd-d7da-4e62-86f4-571e457e1bf9/2013-12-05_1120.png
如果您的代码中有一部分可能会占用大量内存,则无需更改 php.ini
文件中的 memory_limit
值,您可以在该部分运行之前删除 memory_limit
,然后再将其替换。
$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
在 Drupal 7 中,您可以修改位于您的站点/默认文件夹中的 settings.php 文件中的内存限制。在第 260 行附近,您将看到:
ini_set('memory_limit', '128M');
即使您的 php.ini 设置足够高,如果您的 Drupal settings.php 文件中没有设置,您也无法消耗超过 128 MB。
只需在网页顶部添加 ini_set('memory_limit', '-1');
行。
您可以根据需要将内存设置为-1,到16M
等。
对于 Drupal 用户,这个 Chris Lane 的回答是:
ini_set('memory_limit', '-1');
有效,但我们需要在开幕式之后放置
<?php
站点根目录中 index.php 文件中的标记。
更改 php.ini 文件中的内存限制并重新启动 Apache。重新启动后,从任何 PHP 文件运行 phpinfo(); 函数以确认 memory_limit
更改。
memory_limit = -1
内存限制 -1 表示没有设置内存限制。现在是最大值。
PHP 5.3+ 允许您通过在 public_html
文件夹中放置一个 .user.ini
文件来更改内存限制。只需创建上述文件并在其中键入以下行:
memory_limit = 64M
一些 cPanel 主机只接受这种方法。
崩溃页面?
https://i.stack.imgur.com/a2IyM.png
(当 MySQL 必须查询大行时会发生这种情况。默认情况下,memory_limit
设置为 small,这对硬件来说更安全。)
您可以在增加 php.ini
之前检查系统现有内存状态:
# free -m
total used free shared buffers cached
Mem: 64457 63791 666 0 1118 18273
-/+ buffers/cache: 44398 20058
Swap: 1021 0 1021
在这里我增加了它,如下所示,然后执行 service httpd restart
来修复崩溃页面问题。
# grep memory_limit /etc/php.ini
memory_limit = 512M
free -m
命令决定新的 memory_limit 后应该查看哪个数字(行和列?)?
对于那些挠头想知道为什么这个小函数会导致内存泄漏的人,有时由于一个小错误,一个函数开始递归地永远调用自己。
例如,一个代理类与将要代理它的对象的函数具有相同的名称。
class Proxy {
private $actualObject;
public function doSomething() {
return $this->actualObjec->doSomething();
}
}
有时你可能会忘记带上那个小实际对象成员,因为代理实际上有那个 doSomething
方法,PHP 不会给你任何错误,对于一个大类,它可能会被隐藏几分钟才能找到找出为什么它会泄漏内存。
die('here')
放入代码中并移动该语句以查看递归的开始位置。
在比以前工作的数据集更小的数据集上运行时,我遇到了以下错误。
致命错误:第 173 行 C:\workspace\image_management.php 中允许的内存大小为 134217728 字节已用尽(尝试分配 4096 字节)
由于寻找故障将我带到这里,我想我会提到它并不总是以前答案中的技术解决方案,而是更简单的东西。就我而言,它是 Firefox。在我运行该程序之前,它已经使用了 1,157 MB。
事实证明,在几天的时间里,我一次只看一段 50 分钟的视频,结果把事情搞砸了。这是专家在没有考虑的情况下纠正的那种修复,但对于我这样的人来说,这是值得牢记的。
使用 yield
也可能是一种解决方案。请参阅 Generator syntax。
有时在循环内实现 yield
可能会解决问题,而不是更改 PHP.ini
文件以获得更大的内存存储。 yield 所做的不是一次转储所有数据,而是一一读取,节省大量内存使用。
PHP.ini
?不是php.ini
吗?
在我的 mac (Catalina - Xampp) 上,没有加载文件,所以我必须先这样做。
sudo cp /etc/php.ini.default /etc/php.ini
sudo nano /etc/php.ini
然后更改 memory_limit = 512M
然后重新启动Apache并检查文件是否加载
php -i | grep php.ini
结果是
Configuration File (php.ini) Path => /etc
Loaded Configuration File => /etc/php.ini
最后检查
php -r "echo ini_get('memory_limit').PHP_EOL;"
像这样运行脚本(例如 cron 案例):php5 /pathToScript/info.php
会产生相同的错误。
正确方法:php5 -cli /pathToScript/info.php
如果您运行的是 WHM 驱动的 VPS(虚拟专用服务器),您可能会发现您没有权限直接编辑 PHP.INI;系统必须这样做。在 WHM 主机控制面板中,进入 Service Configuration → PHP Configuration Editor 并修改 memory_limit
:
https://i.stack.imgur.com/kewYS.png
我发现在实际处理的文件中包含或要求 _dbconnection.php_
和 _functions.php
时它很有用,而不是包含在标题中。它本身就包含在内。
因此,如果包含页眉和页脚,只需在包含页眉之前包含所有功能文件。
对我来说,此错误消息的最常见原因是在 PHP“for”语句中省略了“++”运算符。无论您允许使用多少内存,这都会导致循环永远继续。这是一个简单的语法错误,但编译器或运行时系统很难检测到。如果我们想寻找它,我们很容易纠正!
但是,假设您想要一个提前停止此类循环并报告错误的通用过程?您可以简单地检测每个循环(或至少是最内层的循环),如下所述。
在某些情况下,例如异常内的递归,set_time_limit
失败,并且浏览器不断尝试加载 PHP 输出,无论是无限循环还是致命错误消息,这是本问题的主题。
通过减少代码开头附近允许的分配大小,您可以防止致命错误,如其他答案中所述。
然后你可能会留下一个终止的程序,但仍然难以调试。
无论您的程序是否终止,通过在您的程序中插入 BreakLoop()
调用来检测您的代码以获得控制并找出您的程序中的哪个循环或递归导致了问题。
BreakLoop的定义如下:
function BreakLoop($MaxRepetitions=500,$LoopSite="unspecified")
{
static $Sites=[];
if (!@$Sites[$LoopSite] || !$MaxRepetitions)
$Sites[$LoopSite]=['n'=>0, 'if'=>0];
if (!$MaxRepetitions)
return;
if (++$Sites[$LoopSite]['n'] >= $MaxRepetitions)
{
$S=debug_backtrace(); // array_reverse
$info=$S[0];
$File=$info['file'];
$Line=$info['line'];
exit("*** Loop for site $LoopSite was interrupted after $MaxRepetitions repetitions. In file $File at line $Line.");
}
} // BreakLoop
$LoopSite 参数可以是代码中函数的名称。这不是必需的,因为您将收到的错误消息会将您指向包含 BreakLoop() 调用的行。
就我而言,这是编写函数方式的一个简短问题。为函数的输入变量分配新值可能会导致内存泄漏,例如:
/**
* Memory leak function that illustrates unintentional bad code
* @param $variable - input function that will be assigned a new value
* @return null
**/
function doSomehting($variable){
$variable = 'set value';
// Or
$variable .= 'set value';
}
https://i.stack.imgur.com/WaUHE.png
这对服务器来说太危险了 您的 PHP 代码可能在某处存在内存泄漏,而您正在告诉服务器只使用它想要的所有内存。你根本不会解决问题。如果您监视您的服务器,您会发现它现在可能正在使用大部分 RAM,甚至交换到磁盘。
我花了两个时间寻找解决方案,我发现这是我打电话时与 PDO 通话的原因
$stmt->bindParam(":PERIOD", $period);
变量周期是
empty string ''
所以这个问题可能有多个根本原因,我对你的建议是尝试反复试验或二分法来寻找根本原因,删除代码并尝试搜索失败的行代码
更新:我使用 $pdo->query() 方法也遇到了这个错误我使用了 $pdo->prepare() 并且运行良好,所以,虽然我有
$sql = "SELECT * FROM COURSE_DETAILS where ACTIVE = 1 AND COURSE_DETAILS_ID = $id";
$stmt = getConnection()->query($sql);
$courseDetails = $stmt->fetchAll(PDO::FETCH_ASSOC)
然后我把它改成
$sql = "SELECT * FROM COURSE_DETAILS where ACTIVE = 1 AND COURSE_DETAILS_ID = ?";
$stmt = getConnection()->prepare($sql);
$stmt->execute(array($id));
神奇地内存错误消失了!
这个问题是几年前问的,所以虽然我的事实不一样,但我最近在循环解析服务器上的html表时也收到了这个错误。错误的原因是因为使用连接到变量 $table
的双引号来解析 html,同时在多行上连接字符串。
$table = "<table>\n";
$table .= "<tbody>\n";
foreach ($data as $item) {
$table .= "<tr>\n";
// this caused the Fatal Error: Allowed Memory Size of 134217728 Bytes Exhausted
$table .= "<td>$item->description</td><td>$item->qty</td>" .
$table .= "<td>$item->price_incl</td><td>$item->price->vat</td>" .
$table .= "<td>$item->price_excl</td><td>$item->available</td>" .
$table .= "</tr>\n";
}
$table .= "</tbody>";
$table .= "</table>\n";
上面的错误对我来说很奇怪,但考虑到我已经将字符串连接到 $table
变量,现在我尝试过这个似乎很愚蠢。解决方案是删除第 7 行和第 8 行的变量连接或删除第 6、7 和 8 行的结束连接符号。
-1
的值可能仅在开发环境中用于测试目的。