在 PHP 中安全地捕获“允许的内存大小已用尽”错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8440439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 04:42:57  来源:igfitidea点击:

Safely catch a 'Allowed memory size exhausted' error in PHP

phpmemory

提问by Matt R. Wilson

I have a gateway script that returns JSON back to the client. In the script I use set_error_handlerto catch errors and still have a formatted return.

我有一个网关脚本,可将 JSON 返回给客户端。在脚本中,我使用set_error_handler来捕获错误并且仍然具有格式化的返回。

It is subject to 'Allowed memory size exhausted' errors, but rather than increase the memory limit with something like ini_set('memory_limit', '19T'), I just want to return that the user should try something else because it used to much memory.

它受“允许的内存大小耗尽”错误的影响,但我不想使用诸如ini_set('memory_limit', '19T') 之类的东西来增加内存限制,我只想返回用户应该尝试其他东西,因为它过去很多记忆。

Are there any good ways to catch fatal errors?

有什么好的方法可以捕获致命错误吗?

采纳答案by Dan Lugg

As this answersuggests, you can use register_shutdown_function()to register a callback that'll check error_get_last().

正如这个答案所暗示的那样,您可以使用register_shutdown_function()注册一个回调来检查error_get_last().

You'll still have to manage the output generated from the offending code, whether by the @(shut up) operator, or ini_set('display_errors', false)

您仍然必须管理从违规代码生成的输出,无论是由@( shut up) 操作员,还是ini_set('display_errors', false)

ini_set('display_errors', false);

error_reporting(-1);

set_error_handler(function($code, $string, $file, $line){
        throw new ErrorException($string, null, $code, $file, $line);
    });

register_shutdown_function(function(){
        $error = error_get_last();
        if(null !== $error)
        {
            echo 'Caught at shutdown';
        }
    });

try
{
    while(true)
    {
        $data .= str_repeat('#', PHP_INT_MAX);
    }
}
catch(\Exception $exception)
{
    echo 'Caught in try/catch';
}

When run, this outputs Caught at shutdown. Unfortunately, the ErrorExceptionexception object isn't thrown because the fatal error triggers script termination, subsequently caught only in the shutdown function.

运行时,输出Caught at shutdown. 不幸的是,ErrorException没有抛出异常对象,因为致命错误触发了脚本终止,随后仅在关闭函数中捕获。

You can check the $errorarray in the shutdown function for details on the cause, and respond accordingly. One suggestion could be reissuing the request back against your web application (at a different address, or with different parameters of course) and return the captured response.

您可以检查$error关闭函数中的数组以了解原因的详细信息,并做出相应的响应。一个建议可能是针对您的 Web 应用程序重新发出请求(在不同的地址,或者当然使用不同的参数)并返回捕获的响应。

I recommend keeping error_reporting()high (a value of -1) though, and using (as others have suggested) error handling for everything else with set_error_handler()and ErrorException.

不过,我建议保持error_reporting()高(-1),并使用(正如其他人建议的那样)使用set_error_handler()和对其他所有内容进行错误处理ErrorException

回答by Alain Tiemblo

If you need to execute business code when this error happens (logging, backup of the context for future debugs, emailing or such), registering a shutdown function is not enough: you should free memory in a way.

如果您需要在发生此错误时执行业务代码(日志记录、备份上下文以备将来调试、发送电子邮件等),仅注册关闭函数是不够的:您应该以某种方式释放内存。

One solution is to allocate some emergency memory somewhere:

一种解决方案是在某处分配一些紧急内存:

public function initErrorHandler()
{
    // This storage is freed on error (case of allowed memory exhausted)
    $this->memory = str_repeat('*', 1024 * 1024);

    register_shutdown_function(function()
    {
        $this->memory = null;
        if ((!is_null($err = error_get_last())) && (!in_array($err['type'], array (E_NOTICE, E_WARNING))))
        {
           // $this->emergencyMethod($err);
        }
    });
    return $this;
}

回答by Christopher Pelayo

you could get the size of the memory already consumed by the process by using this function memory_get_peak_usage documentations are at http://www.php.net/manual/en/function.memory-get-peak-usage.phpI think it would be easier if you could add a condition to redirect or stop the process before the memory limit is almost reached by the process. :)

您可以通过使用此函数来获取进程已消耗的内存大小 memory_get_peak_usage 文档位于http://www.php.net/manual/en/function.memory-get-peak-usage.php我认为它会如果您可以在进程几乎达到内存限制之前添加一个条件来重定向或停止进程,那么会更容易。:)

回答by hpaknia

While @alain-tiemblo solution works perfectly, I put this script to show how you can reserve some memory in a php script, out of object scope.

虽然@alain-tiemblo 解决方案运行良好,但我将这个脚本用于展示如何在 php 脚本中保留一些超出对象范围的内存。

Short Version

精简版

// memory is an object and it is passed by reference
function shutdown($memory) {
    // unsetting $memory does not free up memory
    // I also tried unsetting a global variable which did not free up the memory
    unset($memory->reserve);
}

$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('?', 1024 * 1024);

register_shutdown_function('shutdown', $memory);

Full Sample Script

完整示例脚本

<?php

function getMemory(){
    return ((int) (memory_get_usage() / 1024)) . 'KB';
}

// memory is an object and it is passed by reference
function shutdown($memory) {
    echo 'Start Shut Down: ' . getMemory() . PHP_EOL;

    // unsetting $memory does not free up memory
    // I also tried unsetting a global variable which did not free up the memory
    unset($memory->reserve);

    echo 'End Shut Down: ' . getMemory() . PHP_EOL;
}

echo 'Start: ' . getMemory() . PHP_EOL;

$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('?', 1024 * 1024);

echo 'After Reserving: ' . getMemory() . PHP_EOL;

unset($memory);

echo 'After Unsetting: ' . getMemory() . PHP_EOL;

$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('?', 1024 * 1024);

echo 'After Reserving again: ' . getMemory() . PHP_EOL;

// passing $memory object to shut down function
register_shutdown_function('shutdown', $memory);

And the output would be:

输出将是:

Start: 349KB
After Reserving: 3426KB
After Unsetting: 349KB
After Reserving again: 3426KB
Start Shut Down: 3420KB
End Shut Down: 344KB