【问题标题】:strripos Allowed memory size of 134217728 bytes exhausted (tried to allocate 58887728 bytes)strripos 允许的内存大小为 134217728 字节用尽(尝试分配 58887728 字节)
【发布时间】:2017-08-11 11:42:31
【问题描述】:

我得到了这个代码:

function importXML($filename){
    if(file($filename)) $xml_file = file($filename);
    else umar(97);

    $content = substr($xml_file[1], strpos($xml_file[1], "<office:spreadsheet>"), strpos($xml_file[1], "</office:spreadsheet>") - strpos($xml_file[1], "<office:spreadsheet>"));
    $arr = array();
    $arr2 = array();
    $check = strripos($content, "</table:table-row>") + 18;
    $offset = 0;

    while($offset != $check){
        $start2 = strpos($content, "<table:table-row ", $offset);
        $end2 = strpos($content, "</table:table-row>", $offset);
        $offset = $end2 + 18;


        array_push($arr2, $start2, $end2);
        array_push($arr, substr($content, $start2, $end2-$start2));
    }

    return json_encode($arr);


}

还有这一行

$check = strripos($content, "</table:table-row>") + 18;

返回错误

允许的内存大小134217728字节用尽(尝试分配58887728字节)

我正在处理 58893135 字节的文件。

在 php.ini 中设置

memory_limit=128M

所以我想知道为什么会出现此错误,以及如何摆脱它。

【问题讨论】:

  • 曾经不得不编写while (true) 行代码是一个危险信号。
  • 谢谢,我改成while($offset != $check),如果在循环里面就删了。不知道我为什么这样做,但主要问题仍然存在。

标签: php


【解决方案1】:

试试这个!您可以添加此行:

ini_set(”memory_limit","80M");  

之前

$check = strripos($content, "</table:table-row>") + 18;

【讨论】:

  • 使用 OPs 当前代码 80 mb 太低了。除非进行优化,否则我建议的最低容量为 200MB。
【解决方案2】:

正如 apokryfos 在评论中提到的,我们不应该使用类似的东西:

while(1)

while(true)

while(1===1)

或类似的东西。

虽然如果你必须使用它,那么在这种情况下, 我相信正在发生的事情是您的 while 循环以某种方式永远不会出现在您的 break 语句中,这意味着您的条件:($offset == $check) 永远不会成立。

作为验证,请尝试在 while 之前启动计数器: $count = 1; 在 while 结束时增加它并在假设为 50 时施加中断条件:

while(true){
    //your code
    if($count === 50){
        break;
     }
     $count++;
}

现在如果问题解决了,那么肯定是因为您的 if 中断条件永远不会成立..尝试考虑所有方面并添加更多条件。

如果不是这样,那么我相信您的应用需要的内存比您提供的更多。现在我强烈反对这种方法,但在你发现真正的问题之前,你可以补充:

ini_set('memory_limit','256M');

在代码的开头。

但我再次提到,这是暂时的,您的代码中存在问题,增加内存限制不是解决方案,但它会让您有机会找到真正的问题。

【讨论】:

    【解决方案3】:

    让我们稍微分解一下:

    if(file($filename)) $xml_file = file($filename); 
    

    这里 $xml_file 将占用 58MB 。更不用说您正在阅读该文件两次。你应该改为:

    if (file_exists($filename)) $xml_file = file($filename);
    

    下一位:

    $content = substr($xml_file[1], 
               strpos($xml_file[1], "<office:spreadsheet>"), 
               strpos($xml_file[1], "</office:spreadsheet>") - 
                   strpos($xml_file[1], "<office:spreadsheet>")
             ); 
    

    在这里,您在一个大文件中查找&lt;office:spreadsheet&gt; 两次,然后将子字符串存储在$content 中,这也将占用大约58MB。此时您可以这样做:$xml_file = null; 如果您不需要它,则删除对它的引用,即

    $spreadSheetStart = strpos($xml_file[1], "<office:spreadsheet>");
    $spreadSheetLen = strpos($xml_file[1], "</office:spreadsheet>") - $spreadSheetStart;
    $content = substr($xml_file[1], $spreadSheetStart, $spreadSheetLen);
    $xml_file = null;
    

    现在,最后您有一个大小约为 58MB 的数组,您需要将其转换为相同大小的 JSON,同时内存中仍有 $content,这意味着您需要 170MB。你可以再次这样做:

    $content = null; //unset($content); would also work. 
    return json_encode($arr);
    

    关于 PHP 以及字符串和数组,您需要了解的主要内容是,在 PHP 中,数组和字符串的行为类似于原始类型,这意味着它们总是按值传递。示例:

    $a = "Test"; 
    $b = $a;
    $b .= " again";
    echo $a.PHP_EOL.$b.PHP_EOL;
    

    打印出来:

    测试

    再次测试

    这表明$b$a 的副本,而不是对同一内存位置的引用。副本意味着您通过执行$b = $a; 基本上使代码的内存需求增加了一倍

    数组和所有其他基元(数组、整数、布尔值、浮点数)也是如此。

    【讨论】:

    • 谢谢,现在几乎一切正常,但return json_encode($arr); 告诉我们允许的内存大小为 134217728 字节已用尽(尝试分配 31461376 字节)。设置ini_set('memory_limit','256M'); 有效,但就我看到的其他回复而言,这不是最好的解决方案。所以我很困惑是将这个数组分成两个,并以某种方式单独发送,或者扩展内存限制更好。
    • 不幸的是,我找不到任何关于json_encode 的内存复杂性的文档。如果是2n(即输入大小的两倍),那么您别无选择,只能增加内存限制。
    • 非常感谢,但是如果我增加内存限制,即256M,我应该在函数执行后将其降低到默认值,还是不?
    • @Straighter 我认为你应该这样做。理想情况下,您的内存限制应该是调试代码以确保其最佳的另一种方法。例如,在这种情况下,确实需要增加它,但是如果您在普通用例中耗尽内存,这不应该需要更多内存,这会告诉您周围存在错误。
    猜你喜欢
    • 2016-04-18
    • 2013-08-09
    • 2021-12-29
    • 2017-07-23
    • 2018-02-23
    • 2014-11-20
    • 2017-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多