【问题标题】:PHP - memory exhausted error randomly trigerred on production serverPHP - 在生产服务器上随机触发内存耗尽错误
【发布时间】:2020-05-10 00:18:06
【问题描述】:

我在生产服务器(PHP 7.0.32)上随机(每天大约 20 次)收到此错误:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 4787537204 bytes)

到目前为止,错误的“存在”相当低,但因为我不确定哪里出了问题,所以我担心未来和更大的问题。

当错误发生时,我在页面顶部运行此代码:

    $curTime = time();
    $dataIds = array("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");

    $end = array();
    $update = array();
    $dbUpdate = array();

    foreach($dataIds as $id) {
        $f = fopen( "./data/{$id}.json", "r");
        if ($f === false) {
            continue;
        }

        $data = fread($f, 1024);
        fclose($f);
        $data = json_decode($data, true);

        if (!isset($data['input'])) {
            continue;
        }

        $endDate = $data['input'][0]['end'];
        $updateDate = $data['input'][0]['start'];
        $dbUpdateDate = $data['input'][0]['update'];

        $end[$id] = ($endDate !== "") ? strtotime($endDate." UTC") : $curTime;
        $update[$id] = ($updateDate !== "") ? strtotime($updateDate." UTC") : $curTime;
        $dbUpdate[$id] = ($dbUpdateDate !== "") ? strtotime($dbUpdateDate." UTC") : $curTime;
    }

并且在这一行触发了错误:

 $end[$id] = ($endDate !== "") ? strtotime($endDate." UTC") : $curTime;

我无法在开发服务器上重现该问题,我不确定是什么原因导致此问题以及如何调试此问题(我无法在生产服务器上启用调试功能)。在致命错误之前没有其他警告或通知。

json 数据文件很小,大约 500 字节。但是,它们是从 cron 作业更新的。所以理论上我可以打开不完整的文件。但在这种情况下,json_decode 会返回 null,因为我已经测试过。

json 示例:

{
    "input":    [{
            "id":   "1",
            "start":    "2019-11-15 06:00:00",
            "end":  "2019-11-18 12:00:00",
            "update":   "2019-11-15 10:52:44"
        }]
}

【问题讨论】:

  • 这个问题归结为:为什么 PHP 试图为看似相对简单的事情分配 4.4 GB 的 RAM。
  • 您确定您的服务器没有被病毒感染,并且没有包含文件,尽管某些手段做得更多?
  • @MartinBarker AFAIK,这不会导致strtotime 请求 4GB。而要影响 PHP 本身,病毒必须处于 sudo 模式,这不太可能。另外,没有其他类似的问题。
  • @MartinPerry 我认为确实不太可能提升权限,任何具有 root 访问权限和 SSH 终端的用户也是病毒的入口点。哦,看有腻子保存的配置,告诉我私钥在哪里,以及它连接到的服务器地址......或者使用非官方 repo 进行 PHP 安装还强制包含可能被利用的内容
  • @MartinBarker 我有来自官方仓库的 PHP 并且 SSH 被锁定到 IP。

标签: php memory


【解决方案1】:

经过一番探索(参见other answer,它从完全不同的切线开始),似乎超过 4 GB RAM 的巨大内存分配发生在strtotime 内部。目前尚不清楚这是否与 PHP 错误 #53502(一个旧的 PHP 5.3 错误,目前标记为已关闭)有关。

然而,改变

$end[$id] = ($endDate !== "") ? strtotime($endDate." UTC") : $curTime;

date_default_timezone_set("UTC");
$end[$id] = ($endDate !== "") ? strtotime($endDate) : $curTime;

摆脱现象,运行平稳。目前尚不清楚$endDate 的任何特定值是否会导致此问题或其他可能的因素。

【讨论】:

    【解决方案2】:

    这是我能想到的唯一合理的解释,请检查我的推断是否属实。

    您的$dataIds 不是小整数,而是实际代码中的数十亿。出于某种原因,PHP 似乎将$this->end 键入为字符串而不是数组。实际上,您正在这样做:

    $stuff='string';
    $stuff[5000000000]='y'; // will allocate 5 GB of RAM, exceeding your 128M maximum 
    

    当你认为你正在这样做时:

    $stuff=new Array();
    $stuff[5000000000]='y'; // will allocate just a few Bytes since it is an asssociative Array
    

    您将不得不挖掘代码的其他部分,以了解为什么会出现这种情况

    【讨论】:

    • 我告诉 PHP 使用带有 $end = array(); 的“数组”(我已经更新了问题)。键是字符串,不应被视为数字。
    • @MartinPerry 不应该在这里可能是正确的术语。我看不到那条线上的其他任何东西可能会要求 4GB 的 RAM,而不是分配。
    • 我还怀疑是否可能存在一些格式错误的日期字符串,这些日期字符串会触发 strtotime(在 PHP 7.0.32 中)请求如此多的内存或导致一些内存泄漏。但是,我不知道如何将日期字符串与致命错误一起输出。
    • = array() 是一个二进制数组,new Array(); 实际上是一个HashMap 它是一个围绕堆数据的管理对象
    • @MartinPerry 多么不寻常。在我看来,下一步是将三元表达式交换为单独行上的 if-then 构造: if ($endDate !="") { $tmp=strtotime($endDate." UTC"); } 其他 { $tmp=$curTime } $end[$id]=$tmp.此外,时区曾经存在内存泄漏,但那是几年前的事了(PHP 5.3,错误 #53502)。可能仍然值得尝试:使用 date_default_timezone_set("UTC");而不是附加“UTC”字符串。
    【解决方案3】:

    您可能希望将其添加到您的脚本中(放在最上面)

     ini_set('memory_limit', '-1');
    

    如果您有 64 或更少的值,您可以稍后将 -1 设置为 256 之类的值,这还不够。

    【讨论】:

    • 这不是真正的解决方案,更像是一个修补程序。这并不能解决问题出在哪里以及为什么会出现。
    • 在上面的评论之后,发生的错误将导致服务器崩溃,因为它将耗尽内存。
    猜你喜欢
    • 2015-03-18
    • 1970-01-01
    • 2023-03-05
    • 2015-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    相关资源
    最近更新 更多