关于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 相关文章推荐
在数据量大(超过10万)的情况下
Jan 15 PHP
MySQL连接数超过限制的解决方法
Jul 17 PHP
php从右向左/从左向右截取字符串的实现方法
Nov 28 PHP
php 短链接算法收集与分析
Dec 30 PHP
php自定义session示例分享
Apr 22 PHP
ThinkPHP学习笔记(一)ThinkPHP部署
Jun 22 PHP
php中spl_autoload详解
Oct 17 PHP
php使用递归函数实现数字累加的方法
Mar 16 PHP
php的4种常见运行方式
Mar 20 PHP
thinkphp5.1 文件引入路径问题及注意事项
Jun 13 PHP
解决laravel groupBy 对查询结果进行分组出现的问题
Oct 09 PHP
phpquery中文手册
Mar 18 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实现支持SSL连接的SMTP邮件发送类
2015/03/05 PHP
php开发时容易忘记的一些技术细节
2016/02/03 PHP
Yii2中多表关联查询hasOne hasMany的方法
2017/02/15 PHP
Laravel框架实现的上传图片到七牛功能详解
2019/09/06 PHP
js 浮动层菜单收藏
2009/01/16 Javascript
使用JQuery进行跨域请求
2010/01/25 Javascript
JS Excel读取和写入操作(模板操作)实现代码
2010/04/11 Javascript
基于MooTools的很有创意的滚动条时钟动画
2010/11/14 Javascript
利用JS判断用户是否上网(连接网络)
2013/12/23 Javascript
图片放大镜jquery.jqzoom.js使用实例附放大镜图标
2014/06/19 Javascript
Jquery 实现弹出层插件
2015/01/28 Javascript
PHP 数组current和next用法分享
2015/03/05 Javascript
js简单倒计时实现代码
2016/04/30 Javascript
一个可复用的vue分页组件
2017/05/15 Javascript
JavaScript实现多重继承的方法分析
2018/01/09 Javascript
[14:20]刀塔大凶女神互压各路奇葩屌丝
2014/05/16 DOTA
python动态加载变量示例分享
2014/02/17 Python
python实现unicode转中文及转换默认编码的方法
2017/04/29 Python
Python中的id()函数指的什么
2017/10/17 Python
python模拟事件触发机制详解
2018/01/19 Python
Flask框架URL管理操作示例【基于@app.route】
2018/07/23 Python
Python3中的最大整数和最大浮点数实例
2019/07/09 Python
python进阶之自定义可迭代的类
2019/08/20 Python
opencv+python实现均值滤波
2020/02/19 Python
如何在Python对Excel进行读取
2020/06/04 Python
网页中的电话号码如何实现一键直呼效果_附示例
2016/03/15 HTML / CSS
HTML5 实现图片上传预处理功能
2020/02/06 HTML / CSS
澳大利亚波希米亚风时尚品牌:Tree of Life
2019/09/15 全球购物
PHP如何自定义函数
2016/09/16 面试题
请写出 BOOL flag 与"零值"比较的 if 语句
2016/02/29 面试题
医药专业推荐信
2013/11/15 职场文书
销售职业生涯规划范文
2014/03/14 职场文书
气象学专业个人求职信
2014/04/22 职场文书
2015年公司新年寄语
2014/12/08 职场文书
车位出租协议书范本
2016/03/19 职场文书
解决Vmware虚拟机安装centos8报错“Section %Packages Does Not End With %End. Pane Is Dead”
2022/06/01 Servers