关于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 相关文章推荐
Smarty安装配置方法
Apr 10 PHP
使用php+Ajax实现唯一校验实现代码[简单应用]
Nov 29 PHP
浅谈apache和nginx的rewrite的区别
Feb 22 PHP
PHP调用.NET的WebService 简单实例
Mar 27 PHP
php文件操作相关类实例
Jun 18 PHP
php无序树实现方法
Jul 28 PHP
PHP数据库表操作的封装类及用法实例详解
Jul 12 PHP
PHPMailer使用QQ邮箱实现邮件发送功能
Aug 18 PHP
ThinkPHP框架中使用Memcached缓存数据的方法
Mar 31 PHP
PHP操作Postgresql封装类与应用完整实例
Apr 24 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
Mar 23 PHP
php修改word的实例方法
Nov 17 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
与数据库连接
2006/10/09 PHP
如何使用php判断服务器是否是HTTPS连接
2013/07/05 PHP
php实现监听事件
2013/11/06 PHP
PHP获取指定函数定义在哪个文件中以及其所在的行号实例
2014/05/08 PHP
destoon实现不同会员组公司名称显示不同的颜色的方法
2014/08/22 PHP
基于PHP给大家讲解防刷票的一些技巧
2015/11/18 PHP
php简单截取字符串代码示例
2016/10/19 PHP
PHP基于接口技术实现简单的多态应用完整实例
2017/04/26 PHP
PHP实现的MD5结合RSA签名算法实例
2017/10/07 PHP
php实现大文件断点续传下载实例代码
2019/10/01 PHP
JavaScript中yield实用简洁实现方式
2010/06/12 Javascript
Javascript中的delete操作符详细介绍
2014/06/06 Javascript
node.js中的fs.fchmodSync方法使用说明
2014/12/16 Javascript
AngularJS基础知识笔记之表格
2015/05/10 Javascript
VUEJS实战之利用laypage插件实现分页(3)
2016/06/13 Javascript
JS实现改变HTML上文字颜色和内容的方法
2016/12/30 Javascript
Node.js 基础教程之全局对象
2017/08/06 Javascript
vue2.0 实现导航守卫的具体用法(路由守卫)
2018/05/17 Javascript
layui的数据表格+springmvc实现搜索功能的例子
2019/09/28 Javascript
Node.js中文件系统fs模块的使用及常用接口
2020/03/06 Javascript
[00:09]DOTA2新版本PA至宝特效动作展示
2014/11/19 DOTA
python实现折半查找和归并排序算法
2017/04/14 Python
对pandas中apply函数的用法详解
2018/04/10 Python
解决使用pycharm提交代码时冲突之后文件丢失找回的方法
2018/08/05 Python
pandas通过loc生成新的列方法
2018/11/28 Python
基于python历史天气采集的分析
2019/02/14 Python
python3.7 openpyxl 删除指定一列或者一行的代码
2019/10/08 Python
Django 拼接两个queryset 或是两个不可以相加的对象实例
2020/03/28 Python
python 动态绘制爱心的示例
2020/09/27 Python
使用Vue.js和MJML创建响应式电子邮件
2021/03/23 Vue.js
应聘编辑职位自荐信范文
2014/01/05 职场文书
暑期实践思想汇报
2014/01/06 职场文书
售后服务承诺书怎么写
2014/05/21 职场文书
运动会开幕式致辞
2015/07/29 职场文书
员工升职自我评价
2019/03/26 职场文书
Golang map映射的用法
2022/04/22 Golang