why php7 throw Fatal error: Allowed memory size of xx bytes exhausted (tried to allocate xx bytes)

探讨Swoole协程服务器在高并发场景下面临的内存管理挑战,特别是单个协程占用过多内存导致的问题及解决方案。

用swoole 写的协程 server能做到高并发,但这有一个问题必须考虑的,多个协程跑在一个进程内,某个协程把所有的内存吃光了,

其它协程怎么办?这个进程会怎么处理?

 

首先不要管协程这么一回事,php最初设计就是一个脚本引擎,目前php版本实现的是,遇到 内存不足,不是一个 E_RECOVERABLE_ERROR,直接就 调用shutdown 函数了。

当swoole扩展引入协程概念时,还是跑在这个脚本引擎上,

一个协程吃光内存  --触发-->   脚本shutdown  --导致-->   进程内所有资源被回收(包括其它协程)

<?php
register_shutdown_function(function() {
    echo "register_shutdown_function\n";
});

$var = 'clwu';
try {
    new aa;
    $var = @str_repeat($var, 1024*1024*1024*1024);
} catch (\Throwable $e) {
    echo 'OOM',PHP_EOL;
}

上面的实验代码中,new aa 找不到这个类,php 可以catch 到这个异常,但下一行 str_repeat($var, 1024*1024*1024*1024) 会吃光所有内存,抛出一个 php 没有catch 的致命错误:

1)为什么 new aa 找不到这个类抛出的异常可以 catch ?

因为 new aa 是转换成执行php脚本引擎的代码,引擎逻辑有 判断 找不到这个类就 HANDLE_EXCEPTION()

 

2)为什么 str_repeat($var, 1024*1024*1024*1024) 吃光内存的异常没有 catch 呢?因为 php 脚本引擎的逻辑就没有写要去检测有没有异常抛出

php脚本引擎把 str_repeat 转换为一个可以 FAST CALL 的外部函数调用(底层libc库的原生代码),脚本引擎在调用完这个函数时,是执行

ZEND_VM_NEXT_OPCODE()

而不是

ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION()

,see 脚本引擎的设计时就不知道要怎么处理 外部资源的问题。目前php版本的实现是抛出一个致命的错误然后脚本中止。

 

了解了问题的根源,就有了问题的解决方案了,我不要改动 php的源码,因为到时 服务器自动更新到新版本php时又会覆盖我的改动,facebook自己维护的HHVM就不能很好的跟上生态。

还是要自己在协程的最开始主动用 memory_get_usage() 检查进程的内存是否还足够,如果不足够就 拒绝服务,以免把其它协程给坑了。

2025/06/23 14:14:53 [error] 185042025/06/23 14:14:53 [error] 18504#0: *40688 FastCGI sent in stderr:PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 991232 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/monolog/monolog/src/Monolog/Logger.php on line 329 PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php on line 202" while reading response header from upstream, client: 223.104.194.134, server: yijian.sdhuan.cn, request: "POST /addons/yun_shop/api.php?i=1&uuid=0&type=5&shop_id=null&basic_info=1&validate_page=1&scope=home&route=meal.home.getHomeFirstCategory HTTP/2.0", upstream: "fastcgi://unix:/tmp/php-cgi-72.sock:", host: "yijian.sdhuan.cn", referrer: "https://yijian.sdhuan.cn/addons/yun_shop/?menu=" 2025/06/23 14:14:53 [error] 18504#0: *40688 FastCGI sent in stderr:PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 991232 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/monolog/monolog/src/Monolog/Logger.php on line 329 PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php on line 202" while reading response header from upstream, client: 223.104.194.134, server: yijian.sdhuan.cn, request: "POST /addons/yun_shop/api.php?i=1&uuid=0&type=5&shop_id=null&validate_page=1&scope=home&route=meal.home.getHomeThirdCategory HTTP/2.0", upstream: "fastcgi://unix:/tmp/php-cgi-72.sock:", host: "yijian.sdhuan.cn", referrer: "https://yijian.sdhuan.cn/addons/yun_shop/?menu=" 2025/06/23 14:14:53 [error] 18504#0: *40688 FastCGI sent in stderr:PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 991232 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/monolog/monolog/src/Monolog/Logger.php on line 329 PHP message: PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /home/data/baota/wwwroot/yijian.sdhuan.cn/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php on line 43" while reading response header from upstream, client: 223.104.194.134, server: yijian.sdhuan.cn, request: "POST /addons/yun_shop/api.php?i=1&uuid=0&type=5&shop_id=null&validate_page=1&scope=home&route=coupon.member-coupon.coupons-for-member HTTP/2.0", upstream: "fastcgi://unix:/tmp/php-cgi-72.sock:", host: "yijian.sdhuan.cn", referrer: "https://yijian.sdhuan.cn/addons/yun_shop/?menu="报错是什么意思,怎么解决
06-24
### 解决PHP内存耗尽错误问题 在PHP中,当程序运行时占用的内存量超过了`memory_limit`设置的限制时,会抛出“Allowed memory size exhausted”的致命错误。这个问题可能出现在多种场景下,例如Laravel、Monolog或Doctrine等框架中。 #### 1. 增加PHP内存限制 可以通过修改`php.ini`文件中的`memory_limit`参数来增加允许的最大内存使用量。例如,将`memory_limit`设置为更高的值: ```ini memory_limit = 512M ``` 如果无法直接修改`php.ini`文件,也可以通过以下方法动态调整内存限制: - 在代码中使用`ini_set`函数: ```php ini_set('memory_limit', '512M'); ``` 此方法仅对当前脚本有效[^1]。 - 使用`.htaccess`文件(适用于Apache服务器): ```apache php_value memory_limit 512M ``` #### 2. 捕获内存溢出错误 尽管可以通过增加内存限制来解决大部分问题,但在某些情况下仍需捕获内存溢出错误以避免程序崩溃。可以使用`try-catch`块捕获异常,但需要注意PHP的致命错误通常不会被捕获。为了处理这种情况,可以注册一个自定义错误处理器: ```php set_error_handler(function($errno, $errstr) { if (strpos($errstr, 'Allowed memory size') !== false) { error_log('Memory limit exceeded: ' . $errstr); // 自定义逻辑,例如发送警报邮件或记录日志 } }); ``` 此外,在循环或其他可能导致内存泄漏的操作中,应定期检查内存使用情况: ```php if (memory_get_usage() > 400 * 1024 * 1024) { // 如果内存使用超过400MB throw new \Exception('Memory usage is too high'); } ``` #### 3. Laravel中的内存优化 在Laravel中,内存溢出问题可能与日志记录(Monolog)、数据库操作(Doctrine)或队列任务有关。以下是针对这些场景的解决方案: - **Monolog**:确保日志文件不会无限增长。可以通过配置`logging.php`中的`daily`驱动来限制日志文件的数量和大小: ```php 'default' => env('LOG_CHANNEL', 'stack'), 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['single'], 'days' => 7, // 保留最近7天的日志 ], ], ``` - **Doctrine**:在批量处理数据时,关闭持久化上下文以释放内存: ```php $entityManager->clear(); ``` - **队列任务**:对于长时间运行的任务,建议将其拆分为多个小任务,并通过队列系统逐步执行。 #### 4. Composer相关问题 如果在运行Composer时遇到内存不足的问题,可以指定PHP二进制文件路径并增加内存限制: ```bash /usr/local/php71/bin/php -d memory_limit=512M /usr/local/bin/composer install ``` 或者直接在命令行中增加内存限制: ```bash COMPOSER_MEMORY_LIMIT=-1 composer install ``` #### 5. 其他注意事项 确保服务器有足够的可用内存。如果运行的是虚拟机或容器环境,可能需要调整分配给该环境的资源。 --- ### 示例代码 以下是一个示例,展示如何动态调整内存限制并在循环中检查内存使用情况: ```php <?php ini_set('memory_limit', '256M'); function processLargeData() { $content = ''; while (true) { $content .= str_repeat('a', 1024 * 1024); // 每次增加1MB数据 if (memory_get_usage() > 200 * 1024 * 1024) { // 如果内存使用超过200MB error_log('Memory usage exceeded the limit'); break; } } } processLargeData(); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值