關(guān)于Yii2框架跑腳本時內(nèi)存泄漏問題的分析與解決
現(xiàn)象
在跑 edu_ocr_img 表的歸檔時,每跑幾萬個數(shù)據(jù),都會報一次內(nèi)存耗盡
PHP Fatal error: Allowed memory size of 134217728 bytesexhausted (tried toallocate 135168 bytes)
跟蹤代碼發(fā)現(xiàn),是在插入時以下代碼造成的:
EduOCRTaskBackup::getDb()->createCommand()->batchInsert(EduOCRTaskBackup::tableName(), $fields, $data)->execute();
execute 之后會造成使用內(nèi)存漲上去,并且在之后 unset 所有變量內(nèi)存也會有一部分不會刪除,直到內(nèi)存耗盡。
于是跟蹤到 Yii2中execute的具體代碼塊發(fā)現(xiàn)在記錄 log 的時候會將使用很高的內(nèi)存,分析代碼之后得出造成泄漏的代碼塊如下:
造成泄漏的代碼塊
/**
* 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;
}
}
}
}
// 這里是造成內(nèi)存的罪魁禍首
$this->messages[] = [$message, $level, $category, $time, $traces];
if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
$this->flush();
}
}
造成內(nèi)存泄漏的原因分析
在 Yii2框架中的 vendor/yiisoft/yii2/log/Logger.php:156 log函數(shù)的156行之后會判斷 count($this->messages) >= $this->flushInterval
即:內(nèi)存中存儲的 message 的條數(shù)要大于等于預(yù)設(shè)的 $this->flushInterval 才會將內(nèi)存中的message 刷到磁盤上去。
如果在刷新到磁盤之前就已經(jīng)將 php.ini 設(shè)置的 128M 內(nèi)存打滿的話,會直接報錯申請內(nèi)存耗盡。
很多關(guān)于 YII2其他原因的內(nèi)存泄漏的討論
https://github.com/yiisoft/yii2/issues/13256
解決方案
在程序開始時,設(shè)置 flushInterval 為一個比較小的值
\Yii::getLogger()->flushInterval = 100; // 設(shè)置成一個較小的值
在程序執(zhí)行過程中,每次 execute 之后對內(nèi)存中的 message 進行 flush
\Yii::getLogger()->flush(true); // 參數(shù)傳 true 表示每次都會將 message 清理到磁盤中
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
php提取身份證號碼中的生日日期以及驗證是否為成年人的函數(shù)
本篇文章使用php技術(shù)提取身份證號碼中的生日日期來判斷是否是未成年人的一個函數(shù)。下面小編把代碼分享給大家,供大家參考2015-09-09
Laravel如何實現(xiàn)適合Api的異常處理響應(yīng)格式
這篇文章主要給大家介紹了關(guān)于Laravel如何實現(xiàn)適合Api的異常處理響應(yīng)格式的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Laravel具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Zend Framework教程之Zend_Config_Ini用法分析
這篇文章主要介紹了Zend Framework教程之Zend_Config_Ini用法,較為詳細的分析了Zend_Config_Ini操作配置數(shù)據(jù)的相關(guān)技巧,需要的朋友可以參考下2016-03-03

