关于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 相关文章推荐
如何将一个表单同时提交到两个地方处理
Oct 09 PHP
php为什么选mysql作为数据库? Mysql 创建用户方法
Jul 02 PHP
PHP基于GD库的缩略图生成代码(支持jpg,gif,png格式)
Jun 19 PHP
ThinkPHP3.1的Widget新用法
Jun 19 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(六)
Jun 23 PHP
Laravel中使用自己编写类库的3种方法
Feb 10 PHP
ThinkPHP框架表单验证操作方法
Jul 19 PHP
Laravel中七个非常有用但很少人知道的Carbon方法
Sep 21 PHP
PHP PDOStatement::debugDumpParams讲解
Jan 30 PHP
PHP 7.4中使用预加载的方法详解
Jul 08 PHP
laravel框架中控制器的创建和使用方法分析
Nov 23 PHP
PHP $O00OO0=urldecode & eval 解密,记一次商业源码的去后门
Sep 13 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验证码函数代码(简单实用)
2013/09/29 PHP
php foreach循环中使用引用的问题
2013/11/06 PHP
php中current、next与reset函数用法实例
2014/11/17 PHP
PHP实现获取中英文首字母
2015/06/19 PHP
autoPlay 基于jquery的图片自动播放效果
2011/12/07 Javascript
node.js中的fs.utimes方法使用说明
2014/12/15 Javascript
详解JavaScript中常用的函数类型
2015/11/18 Javascript
jQuery选择器用法实例详解
2015/12/17 Javascript
详解Javascript中的Object对象
2016/02/28 Javascript
JavaScript知识点总结(四)之逻辑OR运算符详解
2016/05/31 Javascript
Node.js DES加密的简单实现
2016/07/07 Javascript
Vue和Bootstrap的整合思路详解
2017/06/30 Javascript
Vue.js实例方法之生命周期详解
2017/07/03 Javascript
Vue.js 实现微信公众号菜单编辑器功能(一)
2018/05/08 Javascript
解析vue路由异步组件和懒加载案例
2018/06/08 Javascript
jQuery实现表格的增、删、改操作示例
2019/01/27 jQuery
在vue中高德地图引入和轨迹的绘制的实现
2019/10/11 Javascript
详解Vue的watch中的immediate与watch是什么意思
2019/12/30 Javascript
Vue解决echart在element的tab切换时显示不正确问题
2020/08/03 Javascript
[51:17]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第二场 10月30日
2020/10/31 DOTA
Python 描述符(Descriptor)入门
2016/11/20 Python
python之DataFrame实现excel合并单元格
2021/02/22 Python
pandas修改DataFrame列名的方法
2018/04/08 Python
对Python 窗体(tkinter)文本编辑器(Text)详解
2018/10/11 Python
利用Python的sympy包求解一元三次方程示例
2019/11/22 Python
Python实现爬取并分析电商评论
2020/06/19 Python
python爬虫用mongodb的理由
2020/07/28 Python
如何用python实现一个HTTP连接池
2021/01/14 Python
HTML+CSS3+JS 实现的下拉菜单
2020/11/25 HTML / CSS
详解HTML5中download属性的应用
2015/08/06 HTML / CSS
Gap中国官网:美式休闲风服饰
2017/02/05 全球购物
巴西男士胡须和头发护理产品商店:Beard
2017/11/13 全球购物
乌克兰电子产品和家用电器购物网站:TOUCH
2019/08/09 全球购物
中国京东和泰国中央集团合资的网站:JD CENTRAL
2020/08/22 全球购物
大学竞选班长演讲稿
2014/04/24 职场文书
审计局班子四风对照检查材料思想汇报
2014/10/07 职场文书