关于Yii2框架跑脚本时内存泄漏问题的分析与解决

吾爱主题 阅读:111 2021-09-18 16:39:00 评论:0

现象

在跑 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 的时候会将使用很高的内存,分析代码之后得出造成泄漏的代码块如下:

造成泄漏的代码块

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 /**   * 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 为一个比较小的值

?
1 \Yii::getLogger()->flushInterval = 100; // 设置成一个较小的值

在程序执行过程中,每次 execute 之后对内存中的 message 进行 flush

?
1 \Yii::getLogger()-> flush (true); // 参数传 true 表示每次都会将 message 清理到磁盘中

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。

原文链接:https://www.maiyewang.com/2019/11/30/yii2-框架跑脚本时内存泄漏问题分析/

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容