关于Yii2框架跑脚本时内存泄漏问题的分析与解决
现象
在跑edu_ocr_img表的归档时,每跑几万个数据,都会报一次内存耗尽
PHPFatalerror: Allowedmemorysizeof134217728bytesexhausted(triedtoallocate135168bytes)
跟踪代码发现,是在插入时以下代码造成的:
EduOCRTaskBackup::getDb()->createCommand()->batchInsert(EduOCRTaskBackup::tableName(),$fields,$data)->execute();
execute之后会造成使用内存涨上去,并且在之后unset所有变量内存也会有一部分不会删除,直到内存耗尽。
于是跟踪到Yii2中execute的具体代码块发现在记录log的时候会将使用很高的内存,分析代码之后得出造成泄漏的代码块如下:
造成泄漏的代码块
/** *Logsamessagewiththegiventypeandcategory. *If[[traceLevel]]isgreaterthan0,additionalcallstackinformationabout *theapplicationcodewillbeloggedaswell. *@paramstring|array$messagethemessagetobelogged.Thiscanbeasimplestringoramore *complexdatastructurethatwillbehandledbya[[Target|logtarget]]. *@paraminteger$levelthelevelofthemessage.Thismustbeoneofthefollowing: *`Logger::LEVEL_ERROR`,`Logger::LEVEL_WARNING`,`Logger::LEVEL_INFO`,`Logger::LEVEL_TRACE`, *`Logger::LEVEL_PROFILE_BEGIN`,`Logger::LEVEL_PROFILE_END`. *@paramstring$categorythecategoryofthemessage. */ publicfunctionlog($message,$level,$category='application') { $time=microtime(true); $traces=[]; if($this->traceLevel>0){ $count=0; $ts=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); array_pop($ts);//removethelasttracesinceitwouldbetheentryscript,notveryuseful foreach($tsas$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:156log函数的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清理到磁盘中
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。