关于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 相关文章推荐
最令PHP初学者们头痛的十四个问题
Jan 15 PHP
支持php4、php5的mysql数据库操作类
Jan 10 PHP
php表单转换textarea换行符的方法
Sep 10 PHP
使用PHP遍历文件夹与子目录的函数代码
Sep 26 PHP
PHP开发微信支付的代码分享
May 25 PHP
PHP 二维数组根据某个字段排序的具体实现
Jun 03 PHP
PHP的Socket通信之UDP通信实例
Jul 02 PHP
php里array_work用法实例分析
Jul 13 PHP
对比PHP对MySQL的缓冲查询和无缓冲查询
Jul 01 PHP
yii2高级应用之自定义组件实现全局使用图片上传功能的方法
Oct 08 PHP
PHP合并数组的2种方法小结
Nov 24 PHP
PHP PDOStatement::columnCount讲解
Jan 30 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/04 星际争霸
php数组函数序列 之array_count_values() 统计数组中所有值出现的次数函数
2011/10/29 PHP
基于session_unset与session_destroy的区别详解
2013/06/03 PHP
PHP获取windows登录用户名的方法
2014/06/24 PHP
php中删除数组的第一个元素和最后一个元素的函数
2015/03/07 PHP
超级强大的表单验证
2006/06/26 Javascript
网页中实现浏览器的最大,最小化和关闭按钮
2007/03/12 Javascript
自写简单JS判断是否已经弹出页面
2010/10/20 Javascript
js 获取、清空input type="file"的值示例代码
2014/02/19 Javascript
Node.js异步I/O学习笔记
2014/11/04 Javascript
JS原型链怎么理解
2016/06/27 Javascript
js制作可以延时消失的菜单
2017/01/13 Javascript
jQuery操作css样式
2017/05/15 jQuery
vue2 中如何实现动态表单增删改查实例
2017/06/09 Javascript
微信小程序 swiper组件构建轮播图的实例
2017/09/20 Javascript
基于 Vue.js 之 iView UI 框架非工程化实践记录(推荐)
2017/11/21 Javascript
使用svg实现动态时钟效果
2018/07/17 Javascript
使用element-ui的el-menu导航选中后刷新页面保持当前选中状态
2019/07/19 Javascript
[02:16]DOTA2超级联赛专访Burning 逆袭需要抓住机会
2013/06/24 DOTA
[42:39]老党炸弹人试玩视频
2014/09/03 DOTA
[01:48]完美圣典齐天大圣至宝宣传片
2016/12/17 DOTA
[02:19]2018年度DOTA2最佳核心位选手-完美盛典
2018/12/17 DOTA
Python 互换字典的键值对实例
2019/02/12 Python
Python 通过截图匹配原图中的位置(opencv)实例
2019/08/27 Python
python实现邮件循环自动发件功能
2020/09/11 Python
细说NumPy数组的四种乘法的使用
2020/12/18 Python
美国运动鞋和运动服零售商:Footaction
2017/04/07 全球购物
BIBLOO波兰:捷克的一家在线服装店
2018/03/09 全球购物
如何进行Linux分区优化
2016/09/13 面试题
毕业生应聘幼儿园的自荐信
2013/11/20 职场文书
2014大学校园光棍节活动策划书
2014/09/29 职场文书
2014年家长学校工作总结
2014/11/20 职场文书
2014年公务员个人工作总结
2014/11/22 职场文书
如何利用Matlab制作一款真正的拼图小游戏
2021/05/11 Python
VUE之图片Base64编码使用ElementUI组件上传
2022/04/09 Vue.js
详解SQL报错盲注
2022/07/23 SQL Server