【问题标题】:Running out of memory always on the same line内存不足总是在同一行
【发布时间】:2013-04-10 04:49:58
【问题描述】:

首先,我不是在寻找“检查您的 PHP 内存限制”或“您需要添加更多内存”之类的答案......我在一台专用机器上,有 8GB 的​​ RAMS ;其中512MB是内存限制。我总是在一行出现内存不足错误:

澄清一下:这部分代码属于Joomla!CMS。

function get($id, $group, $checkTime){
    $data = false;
    $path = $this->_getFilePath($id, $group);
    $this->_setExpire($id, $group);
    if (file_exists($path)) {
        $data = file_get_contents($path);
        if($data) {
            // Remove the initial die() statement
            $data   = preg_replace('/^.*\n/', '', $data); // Out of memory here
        }
    }
    return $data;
}

这是 Joomla 缓存的一部分...此函数读取缓存文件并删除阻止直接访问文件的第一行并返回其余数据。

如您所见,该行使用 preg_replace 删除缓存文件中的第一行,该行始终为:

<?php die("Access Denied"); ?>

我的问题是,在我看来,如果初始 $data 很大,它是一个简单的过程(从文件内容中删除第一行)会消耗大量内存吗?如果是这样,解决该问题的最佳方法是什么?我不介意没有 die() 行的缓存文件,我可以采取安全措施并阻止对缓存文件的直接访问。

我错了吗?

更新

正如帖子所建议的那样,正则表达式似乎会产生比解决问题更多的问题。我试过了:

echo memory_get_usage() . "\n";

在正则表达式之后,然后使用substr() 尝试相同的语句。内存使用量的差异非常小。几乎没有。

这是为了你的贡献,我仍在努力找出为什么会发生这种情况。

【问题讨论】:

  • 为什么要使用正则表达式从文件中删除第一行? “现在你有两个问题。”
  • 我认为您的正则表达式正在将所有行替换为空。最终耗尽所有内存。
  • @Wooble 我添加了一个说明,该代码属于 Joomla!
  • @nl-x 不,它只是删除第一行。我之前和之后都转储了 $data,但它没有改变任何其他内容。
  • @AhmadAlfy 在正则表达式期间内存不足的情况下,你怎么能前后转储?我不是正则表达式专家,但我认为您的表达式从行首 (^) 到换行符 (\n) 表示。并且如果你不指定count参数,它会改变所有的出现。

标签: php performance memory optimization joomla


【解决方案1】:

不要使用字符串函数来替换大字符串中的内容。您可以循环浏览文件的行,然后在找到所需内容后中断。 在此处查看 PHP 文档:

http://php.net/manual/en/function.fgets.php

基本上是@cbuckley 刚才所说的:p

【讨论】:

  • 我刚刚意识到我给出了和你一样的答案,在读你的足够好之前。您(和我)的答案与 cbuckley 的答案基本不同,因为我们的答案不涉及一次读取整个文件。 Cbuckley 的回答确实一次读取了整个文件(并将其拆分为一个数组)。
【解决方案2】:

我建议你在包含缓存文件的文件中使用这个:

define('INCLUDESALLOW', 1);

在将包含的文件中:

if( !defined('INCLUDESALLOW') ) die("Access Denied");

然后只需使用include 而不是file_get_contents。这将运行包含的 PHP 代码,但不能 100% 确定这是否是您需要的。

【讨论】:

    【解决方案3】:

    如果您只想删除文件的第一行并返回其余部分,则应使用file

    $lines = file($path);
    array_shift($lines);
    $data = implode("\n", $lines);
    

    【讨论】:

      【解决方案4】:

      使用 substr 来避免内存不足的 preg_replace() ,如下所示:

      $data = substr($data, strpos($data, '?>') + 3);
      

      一般建议不要使用正则表达式,如果您可以通过使用其他字符串/数组函数来完成相同的任务,正则表达式函数比核心字符串/数组函数更慢并且消耗更多内存。

      这在 PHP 文档中也有明确警告,请参阅一些示例:

      http://www.php.net/manual/en/function.preg-match.php#refsect1-function.preg-match-notes http://www.php.net/manual/en/function.preg-split.php#refsect1-function.preg-split-notes

      【讨论】:

      • 现在我知道preg_replace() 是一个内存猪。我会测试并带着结果再来。谢谢
      • 我用memory_get_usage()比较了substrpreg_replace()的内存使用情况,发现差别非常非常小。我已经更新了问题;感谢您的帮助。
      【解决方案5】:

      有时您将使用比 php 分配的 8 MB 更多的内存。如果您无法通过提高代码效率来使用更少的内存,则可能必须增加可用内存。这可以通过两种方式完成。

      限制可以在 php.ini 中设置为全局默认值:

      memory_limit = 32M
      

      或者您可以像这样在脚本中覆盖它:

      <?php
      ini_set('memory_limit', '64M');
      ...
      

      有关 PHP 内存限制的更多信息,您可以查看 This SO questionini.memory-limit

      【讨论】:

      • 问题的第一句话:“首先,我不是在寻找“检查您的 PHP 内存限制”或“您需要添加更多内存”之类的答案... "
      【解决方案6】:

      您应该将fopen()fgets() (http://php.net/fgets) 结合使用,而不是使用一次获取整个文件的file_get_contents(),因为它可能太大而无法运行正则表达式。该函数逐行获取文件。

      然后您可以选择在特定行上执行正则表达式。或者在您的情况下,只需跳过整行。

      所以代替:

      $data = file_get_contents($path);
      if($data) {
          // Remove the initial die() statement
          $data   = preg_replace('/^.*\n/', '', $data); // Out of memory here
      }
      

      试试这个:

      $fileHandler = fopen($path,'r');
      $lineNumber = 0;
      while (($line = fgets($fileHandler)) !== false) {
          if($lineNumber++ != 0) { // Skip the initial die() statement
              $data .= $line; // or maybe echo out $line directly so $data doesn't take up too much memory as well.
          }
      }
      

      【讨论】:

      • 我会试试的,谢谢。我认为$lineNumber++ 缺少; :)
      • 噢!你是对的,对不起。但是请不要调用处理程序 FileLocation,因为它不会尝试告诉您文件在哪里。但它是一个指针,告诉您您在文件中的位置。希望这对你来说很清楚。指针从文件的开头开始。每次调用 fgets() 时,指针都会移动到下一个换行符并返回其间的所有内容。 (顺便说一句。我在 if() 中移动了 lineNumber++)
      • 这个的内存消耗比preg_replace
      • @AhmadAlfy 正如我在最后一行评论的那样,您也可以直接输出数据,而不是 $data .= $line;。这样可以避免将整个文件放入内存。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 2015-05-14
      • 1970-01-01
      相关资源
      最近更新 更多