关于Yii2框架跑脚本时内存泄漏问题的分析与解决


Posted in PHP onDecember 01, 2019

现象

在跑 edu_ocr_img 表的归档时,每跑几万个数据,都会报一次内存耗尽

PHP Fatal error:  Allowed memory size of 134217728 bytesexhausted (tried toallocate 135168 bytes)

跟踪代码发现,是在插入时以下代码造成的:

EduOCRTaskBackup::getDb()->createCommand()->batchInsert(EduOCRTaskBackup::tableName(), $fields, $data)->execute();

execute 之后会造成使用内存涨上去,并且在之后 unset 所有变量内存也会有一部分不会删除,直到内存耗尽。

于是跟踪到 Yii2中execute的具体代码块发现在记录 log 的时候会将使用很高的内存,分析代码之后得出造成泄漏的代码块如下:

造成泄漏的代码块

/**
 * Logs a message with the given type and category.
 * If [[traceLevel]] is greater than 0, additional call stack information about
 * the application code will be logged as well.
 * @param string|array $message the message to be logged. This can be a simple string or a more
 * complex data structure that will be handled by a [[Target|log target]].
 * @param integer $level the level of the message. This must be one of the following:
 * `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`,
 * `Logger::LEVEL_PROFILE_BEGIN`, `Logger::LEVEL_PROFILE_END`.
 * @param string $category the category of the message.
 */
public function log($message, $level, $category = 'application')
{
 $time = microtime(true);
 $traces = [];
 if ($this->traceLevel > 0) {
  $count = 0;
  $ts = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  array_pop($ts); // remove the last trace since it would be the entry script, not very useful
  foreach ($ts as $trace) {
   if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {
    unset($trace['object'], $trace['args']);
    $traces[] = $trace;
    if (++$count >= $this->traceLevel) {
     break;
    }
   }
  }
 }
 
 // 这里是造成内存的罪魁祸首
 $this->messages[] = [$message, $level, $category, $time, $traces];
 if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
  $this->flush();
 }
}

造成内存泄漏的原因分析

在 Yii2框架中的 vendor/yiisoft/yii2/log/Logger.php:156 log函数的156行之后会判断 count($this->messages) >= $this->flushInterval

即:内存中存储的 message 的条数要大于等于预设的 $this->flushInterval 才会将内存中的message 刷到磁盘上去。

如果在刷新到磁盘之前就已经将 php.ini 设置的 128M 内存打满的话,会直接报错申请内存耗尽。

很多关于 YII2其他原因的内存泄漏的讨论
https://github.com/yiisoft/yii2/issues/13256

解决方案

在程序开始时,设置 flushInterval 为一个比较小的值

\Yii::getLogger()->flushInterval = 100; // 设置成一个较小的值

在程序执行过程中,每次 execute 之后对内存中的 message 进行 flush

\Yii::getLogger()->flush(true); // 参数传 true 表示每次都会将 message 清理到磁盘中

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
BBS(php & mysql)完整版(八)
Oct 09 PHP
PHP生成带有雪花背景的验证码
Sep 28 PHP
用php获取本周,上周,本月,上月,本季度日期的代码
Aug 05 PHP
PHP 正则表达式之正则处理函数小结(preg_match,preg_match_all,preg_replace,preg_split)
Oct 05 PHP
php加密算法之实现可逆加密算法和解密分享
Jan 21 PHP
ThinkPHP页面跳转success与error方法概述
Jun 25 PHP
php发送邮件的问题详解
Jun 22 PHP
PHP识别二维码的方法(php-zbarcode安装与使用)
Jul 07 PHP
PHP生成图片缩略图类示例
Jan 12 PHP
php注册审核重点解析(数据访问)
May 23 PHP
PHP创建文件及写入数据(覆盖写入,追加写入)的方法详解
Feb 15 PHP
PHP读取文件,解决中文乱码UTF-8的方法分析
Jan 22 PHP
详解no input file specified 三种解决方法
Nov 29 #PHP
设定php简写功能的方法
Nov 28 #PHP
如何在centos8自定义目录安装php7.3
Nov 28 #PHP
PHP的new static和new self的区别与使用
Nov 27 #PHP
Laravel 微信小程序后端实现用户登录的示例代码
Nov 26 #PHP
Laravel 微信小程序后端搭建步骤详解
Nov 26 #PHP
php 使用expat方式解析xml文件操作示例
Nov 26 #PHP
You might like
谏山创故乡大分县日田市水坝将设立《进击的巨人》立艾伦、三笠以及阿尔敏的铜像!
2020/03/06 日漫
php mssql 数据库分页SQL语句
2008/12/16 PHP
浅谈PHP解析URL函数parse_url和parse_str
2014/11/11 PHP
Symfony的安装和配置方法
2016/03/17 PHP
一个高效的JavaScript压缩工具下载集合
2007/03/06 Javascript
驱动事件的addEvent.js代码
2007/03/27 Javascript
javascript loadScript异步加载脚本示例讲解
2013/11/14 Javascript
ExtJS4中使用mixins实现多继承示例
2013/12/03 Javascript
js弹出div并显示遮罩层
2014/02/12 Javascript
js调用后台、后台调用前台等方法总结
2014/04/17 Javascript
JS创建类和对象的两种不同方式
2014/08/08 Javascript
PHP配置文件php.ini中打开错误报告的设置方法
2015/01/09 PHP
JS中dom0级事件和dom2级事件的区别介绍
2016/05/05 Javascript
JS学习之表格的排序简单实例
2016/05/16 Javascript
JS 实现Base64编码与解码实例详解
2016/11/07 Javascript
JS产生随机数的用法小结
2016/12/10 Javascript
Vue数据驱动模拟实现1
2017/01/11 Javascript
jQuery实现淡入淡出的模态框
2017/02/09 Javascript
微信小程序 数据遍历的实现
2017/04/05 Javascript
详解vue使用$http服务端收不到参数
2019/04/19 Javascript
js 实现ajax发送步骤过程详解
2019/07/25 Javascript
antd design table更改某行数据的样式操作
2020/10/31 Javascript
javascript中call,apply,bind的区别详解
2020/12/11 Javascript
python生成器表达式和列表解析
2016/03/10 Python
python求最大连续子数组的和
2018/07/07 Python
pymysql 插入数据 转义处理方式
2020/03/02 Python
一个不错的HTML5 Canvas多层点击事件监听实例
2014/04/29 HTML / CSS
天巡全球:Skyscanner Global
2017/06/20 全球购物
美国礼品卡交易网站:Cardpool
2018/08/27 全球购物
运动会稿件200字
2014/02/07 职场文书
《赠汪伦》教学反思
2014/04/12 职场文书
班主任个人工作反思
2014/04/28 职场文书
水电工程师岗位职责
2015/02/13 职场文书
起诉意见书范文
2015/05/19 职场文书
nginx简单配置多个server的方法
2021/03/31 Servers
PostgreSQL出现死锁该如何解决
2022/05/30 PostgreSQL