关于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函数解决SQL injection
Oct 09 PHP
第一节--面向对象编程
Nov 16 PHP
PHP转换IP地址到真实地址的方法详解
Jun 09 PHP
PHP ajax 异步执行不等待执行结果的处理方法
May 27 PHP
PHP封装的字符串加密解密函数
Dec 18 PHP
PHP Smarty模版简单使用方法
Mar 30 PHP
thinkPHP连接sqlite3数据库的实现方法(附Thinkphp代码生成器下载)
May 27 PHP
[原创]php实现 data url的图片生成与保存
Dec 04 PHP
PHP mysqli事务操作常用方法分析
Jul 22 PHP
PHP从零开始打造自己的MVC框架之入口文件实现方法详解
Jun 03 PHP
php输出反斜杠的实例方法
Sep 19 PHP
PHP单元测试配置与使用方法详解
Dec 27 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设计模式 Facade(外观模式)
2011/06/26 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
ThinkPHP查询语句与关联查询用法实例
2014/11/01 PHP
Javascript 布尔型分析
2008/12/22 Javascript
EasyUI中的tree用法介绍
2011/11/01 Javascript
JavaScript单元测试ABC
2012/04/12 Javascript
关于JavaScript中的关联数组分析
2013/04/09 Javascript
JS控制输入框内字符串长度
2014/05/21 Javascript
基于jquery实现的可编辑下拉框实现代码
2014/08/02 Javascript
JavaScript异步加载浅析
2014/12/28 Javascript
javascript结合canvas实现图片旋转效果
2015/05/03 Javascript
详解js中==与===的区别
2017/01/08 Javascript
Vue 2.x教程之基础API
2017/03/06 Javascript
node使用UEditor富文本编辑器的方法实例
2017/07/11 Javascript
jQuery实现的简单无刷新评论功能示例
2017/11/08 jQuery
Vue 组件修改根实例的数据的方法
2019/04/02 Javascript
详解滑动穿透(锁body)终极探索
2019/04/16 Javascript
vue + typescript + video.js实现 流媒体播放 视频监控功能
2019/07/07 Javascript
javascript导出csv文件(excel)的方法示例
2019/08/25 Javascript
Vue使用虚拟dom进行渲染view的方法
2019/12/26 Javascript
2款Python内存检测工具介绍和使用方法
2014/06/01 Python
Python代码实现删除一个list里面重复元素的方法
2019/04/02 Python
详解Flask前后端分离项目案例
2020/07/24 Python
python实现xml转json文件的示例代码
2020/12/30 Python
css3 按钮样式简单可扩展创建
2013/03/18 HTML / CSS
美国派对用品及装饰品网上商店:Shindigz
2016/07/30 全球购物
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
购买原创艺术品:Zatista
2019/11/09 全球购物
What is the purpose of Void class? Void类的作用是什么?
2016/10/31 面试题
爱国卫生月活动总结范文
2014/04/25 职场文书
党支部综合考察意见
2015/06/01 职场文书
我的长征观后感
2015/06/09 职场文书
“爱眼护眼,提前预防近视”倡议书3篇
2019/10/30 职场文书
用 Python 定义 Schema 并生成 Parquet 文件详情
2021/09/25 Python
Elasticsearch 数据类型及管理
2022/04/19 Python
TypeScript 内置高级类型编程示例
2022/09/23 Javascript