关于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制作静态网站的模板框架(三)
Oct 09 PHP
用PHP实现ODBC数据分页显示一例
Oct 09 PHP
利用PHP制作简单的内容采集器的原理分析
Oct 01 PHP
PHP 多维数组排序(usort,uasort)
Jun 30 PHP
使用PHP备份MySQL和网站发送到邮箱实例代码
Nov 28 PHP
php根据操作系统转换文件名大小写的方法
Feb 24 PHP
对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
Jul 04 PHP
PHP中IP地址与整型数字互相转换详解
Aug 20 PHP
PHP正则表达式 /i, /is, /s, /isU等介绍
Oct 23 PHP
php连接odbc数据源并保存与查询数据的方法
Dec 24 PHP
PHP全局使用Laravel辅助函数dd
Dec 26 PHP
Apache+PHP+MySQL搭建PHP开发环境图文教程
Aug 06 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笔记之:AOP的应用
2013/04/24 PHP
PHP加密扩展库Mcrypt安装和实例
2013/11/10 PHP
jquery 最简单易用的表单验证插件
2010/02/27 Javascript
Jquery 的扩展方法总结
2011/10/01 Javascript
window.open以post方式将内容提交到新窗口
2012/12/26 Javascript
js取整数、取余数的方法
2014/05/11 Javascript
莱鸟介绍javascript onclick事件
2016/01/06 Javascript
两种方法解决javascript url post 特殊字符转义 + & #
2016/04/13 Javascript
jQuery学习笔记之回调函数
2016/08/15 Javascript
JavaScript中ES6字符串扩展方法
2016/08/26 Javascript
Bootstrap在线电子商务网站实战项目5
2016/10/14 Javascript
xmlplus组件设计系列之文本框(TextBox)(3)
2017/05/03 Javascript
Javascript中的getter和setter初识
2017/08/17 Javascript
使用JQ完成表格隔行换色的简单实例
2017/08/25 Javascript
微信小程序仿微信运动步数排行(交互)
2018/07/13 Javascript
jquery的$().each和$.each的区别
2019/01/18 jQuery
原生js通过一行代码实现简易轮播图
2019/06/05 Javascript
微信小程序中限制激励式视频广告位显示次数(实现思路)
2019/12/06 Javascript
JavaScript适配器模式原理与用法实例详解
2020/03/09 Javascript
详解Python中for循环的使用
2015/04/14 Python
Python 中 Virtualenv 和 pip 的简单用法详解
2017/08/18 Python
恢复百度云盘本地误删的文件脚本(简单方法)
2017/10/21 Python
python登录并爬取淘宝信息代码示例
2017/12/09 Python
python爬虫爬取某站上海租房图片
2018/02/04 Python
python3使用SMTP发送简单文本邮件
2018/06/19 Python
python查看模块,对象的函数方法
2018/10/16 Python
Django实现前台上传并显示图片功能
2020/05/29 Python
可以随进度显示不同颜色的css3进度条分享
2014/04/11 HTML / CSS
将SVG图引入到HTML页面的实现
2019/09/20 HTML / CSS
车贷收入证明范本
2014/01/09 职场文书
心理学专业大学生职业生涯规划范文
2014/02/19 职场文书
幼儿园母亲节活动方案
2014/03/10 职场文书
个人专业技术总结
2015/03/05 职场文书
解决golang结构体tag编译错误的问题
2021/05/02 Golang
MySQL数据库10秒内插入百万条数据的实现
2021/11/01 MySQL
基于Python编写简易版的天天跑酷游戏的示例代码
2022/03/23 Python