关于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 相关文章推荐
PHP5.2下chunk_split()函数整数溢出漏洞 分析
Jun 06 PHP
php 远程图片保存到本地的函数类
Dec 08 PHP
php删除文件夹及其文件夹下所有文件的函数代码
Jan 23 PHP
PHP遍历并打印指定目录下所有文件实例
Feb 10 PHP
PHP读取RSS(Feed)简单实例
Jun 12 PHP
PHP中mysqli_affected_rows作用行数返回值分析
Dec 26 PHP
php递归遍历多维数组的方法
Apr 18 PHP
Yii2.0中的COOKIE和SESSION用法
Aug 12 PHP
yii2简单使用less代替css示例
Mar 10 PHP
PHP设计模式之工厂模式实例总结
Sep 01 PHP
PHP使用SMTP邮件服务器发送邮件示例
Aug 28 PHP
laravel框架创建授权策略实例分析
Nov 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
php项目打包方法
2008/02/18 PHP
Ajax+PHP 边学边练 之二 实例
2009/11/24 PHP
php中获取指定IP的物理地址的代码(正则表达式)
2011/06/23 PHP
php session安全问题分析
2011/06/24 PHP
简单的php缓存类分享     php缓存机制
2014/01/22 PHP
PHP static局部静态变量和全局静态变量总结
2014/03/02 PHP
php 生成自动创建文件夹并上传文件的示例代码
2014/03/07 PHP
Zend Framework入门教程之Zend_Config组件用法详解
2016/12/09 PHP
关于JS中的闭包浅谈
2013/08/23 Javascript
js用Date对象的setDate()函数对日期进行加减操作
2014/09/18 Javascript
jQuery中click事件用法实例
2014/12/26 Javascript
jQuery 中DOM 操作详解
2015/01/13 Javascript
JQuery选择器、过滤器大整理
2015/05/26 Javascript
Jquery 1.9.1源码分析系列(十二)之筛选操作
2015/12/02 Javascript
深入理解Vue.js源码之事件机制
2017/09/27 Javascript
Vue中的无限加载vue-infinite-loading的方法
2018/04/08 Javascript
解决nodejs的npm命令无反应的问题
2018/05/17 NodeJs
element ui 表格动态列显示空白bug 修复方法
2018/09/04 Javascript
使用FormData实现上传多个文件
2018/12/04 Javascript
vue实现输入框的模糊查询的示例代码(节流函数的应用场景)
2019/09/01 Javascript
基于javascript实现贪吃蛇小游戏
2019/11/25 Javascript
[01:34]2016国际邀请赛中国区预选赛IG战队教练采访
2016/06/27 DOTA
python3使用urllib示例取googletranslate(谷歌翻译)
2014/01/23 Python
简述Python2与Python3的不同点
2018/01/21 Python
python根据list重命名文件夹里的所有文件实例
2018/10/25 Python
Python使用LDAP做用户认证的方法
2019/06/20 Python
Python 去除字符串中指定字符串
2020/03/05 Python
css3实现文字扫光渐变动画效果的示例
2017/11/07 HTML / CSS
体验完美剃须:The Art of Shaving
2018/08/06 全球购物
aden + anais英国官网:美国婴儿贴身用品品牌
2019/09/08 全球购物
玩具公司的创业计划书
2013/12/31 职场文书
小区门卫管理制度
2014/01/29 职场文书
清正廉洁演讲稿
2014/05/22 职场文书
乡镇党建工作汇报材料
2014/10/27 职场文书
确保减税降费落地生根,用实实在在措施
2019/07/19 职场文书
Redis实现短信验证码登录的示例代码
2022/06/14 Redis