PHP內(nèi)存溢出的解決方法詳解
什么是內(nèi)存溢出
內(nèi)存溢出是指應(yīng)用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多,最終使得程序運(yùn)行要用到的內(nèi)存大于虛擬機(jī)能提供的最大內(nèi)存。
引起內(nèi)存溢出的原因有很多種,常見的有以下幾種:
1 內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);
2 集合類中有對對象的引用,使用完后未清空;
3 代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;
4 使用的第三方軟件中的BUG;
5 啟動參數(shù)內(nèi)存值設(shè)定的過小;
下面來看些在開發(fā)中經(jīng)常遇到的內(nèi)存溢出的實(shí)例
內(nèi)存溢出經(jīng)常報(bào)錯的情況
PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted
1.處理數(shù)組時(shí)出現(xiàn)內(nèi)存溢出
1)使用迭代生成器,可以通過繼承Iterator接口實(shí)現(xiàn)
2)使用關(guān)鍵詞yield
function?xrange($start,?$end,?$step?=?1)?{ ????for?($i?=?$start;?$i?<=?$end;?$i?+=?$step)?{ ????????yield?$i; ????} } foreach?(xrange(1,?1000000)?as?$num)?{ ????echo?$num,?"\n"; }
2.使用sql查詢數(shù)據(jù),查出來很多,導(dǎo)致內(nèi)存溢出
sql語句在mysql中可以查詢,但是使用php程序查詢就報(bào)php內(nèi)存溢出
1)這個問題在php的官方網(wǎng)站叫緩沖查詢和非緩沖查詢。php的查詢?nèi)笔∧J绞蔷彌_模式。也就是,查詢數(shù)據(jù)結(jié)果一次全部提取到內(nèi)存里供php程序額外的功能,比如說,計(jì)算行數(shù),將指針指向某一行等。
更重要的是程序?qū)?shù)據(jù)集反復(fù)進(jìn)行二次查詢和過濾操作。但這種緩沖查詢模式的缺陷是消耗內(nèi)存,也就是用空間換速度。
2)另外一種查詢模式是非緩沖查詢,數(shù)據(jù)庫服務(wù)器會一條一條的返回?cái)?shù)據(jù),而不是一次全部返回,這樣的結(jié)果是php程序消耗較少的內(nèi)存,但卻增加了數(shù)據(jù)庫服務(wù)器的壓力,因?yàn)閿?shù)據(jù)庫會一直等待php來取數(shù)據(jù),一直到數(shù)據(jù)全部取完。
1.首先查詢數(shù)據(jù)庫需要進(jìn)行l(wèi)imit進(jìn)行分頁查詢
2.如果不使用limit,使用非緩沖查詢
1.mysql:
$conn?=?mysql_connect("localhost",?"user",?"pass"); $db???=?mysql_select_db("world"); $uresult?=?mysql_unbuffered_query("SELECT?Name?FROM?City");????//非緩沖查詢 if?($uresult)?{ ???while?($row?=?mysql_fetch_assoc($uresult))?{ ???????echo?$row['Name']?.?PHP_EOL; ???} }
2.pdo_mysql:
$pdo?=?new?PDO("mysql:host=localhost;dbname=world",?'my_user',?'my_pass'); $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,?false);????//設(shè)置這個屬性,就為非緩沖查詢 $uresult?=?$pdo->query("SELECT?Name?FROM?City"); if?($uresult)?{ ???while?($row?=?$uresult->fetch(PDO::FETCH_ASSOC))?{ ???????echo?$row['Name']?.?PHP_EOL; ???} }
3.mysqli:
$mysqli??=?new?mysqli("localhost",?"user",?"password",?"world"); $uresult?=?$mysqli->query("SELECT?Name?FROM?City",?MYSQLI_USE_RESULT); if?($uresult)?{ ???while?($row?=?$uresult->fetch_assoc())?{ ???????echo?$row['Name']?.?PHP_EOL; ???} } $uresult->close();
3.假定日志中存放的記錄數(shù)為500000條,那么解決方案如下
ini_set(‘memory_limit’,’64M’); //重置php可以使用的內(nèi)存大小為64M,一般在遠(yuǎn)程主機(jī)上是不能修改php.ini文件的,只能通過程序設(shè)置。
注:在safe_mode(安全模式)下,ini_set失效
set_time_limit(600);//設(shè)置超時(shí)限制為6分鐘 $farr?=?$Uarr?=?$Marr?=?$IParr?=?$data?=?$_sub?=?array(); $spt?=?”$@#!$”; $root?=?”/Data/webapps/VisitLog”; $path?=?$dpath?=?$fpath?=?NULL; $path?=?$root.”/”.date(“Y-m”,$timestamp); $dpath?=?$path.”/”.date(“m-d”,$timestamp); for($j=0;$j<24;$j++){ ????$v?=?($j?<?10)???”0″.$j?:?$j; ????$gpath?=?$dpath.”/”.$v.”.php”; ????if(!file_exists($gpath)){ ????????continue; ????}?else?{ ????????$arr?=?file($gpath);////將文件讀入數(shù)組中 ????????array_shift($arr);//移出第一個單元-》 ????????$farr?=?array_merge($farr,$arr); ????????unset($arr); ????} } if(empty($this->farr)){ ????echo?”沒有相關(guān)記錄!”; ????exit; } while(!empty($farr)){ ????$_sub?=?array_splice($farr,?0,?10000);?//每次取出$farr中1000個 ????for($i=0,$scount=count($_sub);$i<$scount;$i++){ ????????$arr?=?explode($spt,$_sub[$i]); ????????$Uarr[]?=?$arr[1];?//vurl ????????$Marr[]?=?$arr[2];?//vmark ????????$IParr[]?=?$arr[3].”?|$nbsp;”.$arr[1];?//IP ????} ????unset($_sub);//用完及時(shí)銷毀 } unset($farr);
這里,不難看出,一方面,我們要增加PHP可用內(nèi)存大小,另一方面,只要我們想辦法對數(shù)組進(jìn)行分批處理,分而治之,將用過的變量及時(shí)銷毀(unset),一般是不會出現(xiàn)溢出問題的。
4.上傳excel文件時(shí),出現(xiàn)內(nèi)存溢出的情況
在文件中分配大點(diǎn)的內(nèi)存設(shè)置內(nèi)存治標(biāo)不治本,而且服務(wù)器的php.ini(memory_limit =128M)有時(shí)候是很難改的。所以在文件中設(shè)置。但是只有php.ini中的安全模式safe_mode開啟時(shí)才可以設(shè)置
ini_set('memory_limit',?'521M');
解決方法:
protected?/extensions/ExcelHelper.php?中945行? $PHPReader->setReadDataOnly(true);??//在拿到數(shù)據(jù)后進(jìn)行設(shè)置只讀 ????public?static?function?importFromExcel($filePath,?$blankRowDel?=?false) ????{ ????????set_time_limit(90); ????????Yii::import('application.extensions.phpexcel.PHPExcel'); ????????$PHPExcel?=?new?PHPExcel(); ????????//默認(rèn)用excel2007讀取excel,若格式不對,則用之前的版本進(jìn)行讀取 ????????$PHPReader?=?new?PHPExcel_Reader_Excel2007(); ????????if?(!$PHPReader->canRead($filePath))?{ ????????????$PHPReader?=?new?PHPExcel_Reader_Excel5(); ????????????if?(!$PHPReader->canRead($filePath))?{ ????????????????throw?new?Exception('can?not?read?the?excel?file'); ????????????} ????????} ????????$PHPReader->setReadDataOnly(true); ????????$PHPExcel??????=?$PHPReader->load($filePath); ????????$allSheetCount?=?$PHPExcel->getSheetCount(); ????????$excelData?????=?array(); ????????for?($currentSheetNum?=?0;?$currentSheetNum?<?$allSheetCount;?$currentSheetNum++)?{ ????????????//讀取excel文件中的第一個工作表 ????????????$currentSheet?=?$PHPExcel->getSheet($currentSheetNum); ????????????//取得當(dāng)前表名 ????????????$currentSheetTitle?=?$currentSheet->getTitle(); ????????????//取得最大的列號 ????????????$allColumn?=?$currentSheet->getHighestColumn(); ????????????//取得一共有多少行 ????????????$allRow?=?$currentSheet->getHighestRow(); ????????????//?從第一行獲取列名 ????????????$currentRow?=?1; ????????????//?從第A列開始輸出 ????????????$colunmNameArray?=?array(); ????????????$max_column_num??=?PHPExcel_Cell::columnIndexFromString($allColumn); ????????????for?($current_column_num?=?0;?$current_column_num?<=?$max_column_num;?$current_column_num++)?{ ????????????????$currentColumn?=?PHPExcel_Cell::stringFromColumnIndex($current_column_num); ????????????????$val???????????=?$currentSheet->getCellByColumnAndRow($current_column_num,?$currentRow)->getValue(); ????????????????if?(empty($val))?{ ????????????????????continue; ????????????????} ????????????????if?(is_object($val))?{ ????????????????????$colunmNameArray[$currentColumn]?=?''; ????????????????????foreach?($val->getRichTextElements()?as?$cell)?{ ????????????????????????$colunmNameArray[$currentColumn]?.=?$cell->getText(); ????????????????????} ????????????????}?else?{ ????????????????????$colunmNameArray[$currentColumn]?=?$val; ????????????????} ????????????} ????????????//從第二行開始輸出,因?yàn)閑xcel表中第一行為列名 ????????????$sheetData?=?array(); ????????????for?($currentRow?=?2;?$currentRow?<=?$allRow;?$currentRow++)?{ ????????????????//從第A列開始輸出?*/ ????????????????$rowData???=?array(); ????????????????$blankCell?=?0; ????????????????for?($current_column_num?=?0;?$current_column_num?<=?$max_column_num;?$current_column_num++)?{ ????????????????????$currentColumn?=?PHPExcel_Cell::stringFromColumnIndex($current_column_num); ????????????????????$val???????????=?$currentSheet->getCellByColumnAndRow($current_column_num,?$currentRow)->getValue(); ????????????????????if?(!isset($colunmNameArray[$currentColumn]))?{ ????????????????????????continue; ????????????????????} ????????????????????//如果輸出漢字有亂碼,則需將輸出內(nèi)容用iconv函數(shù)進(jìn)行編碼轉(zhuǎn)換,如下將gb2312編碼轉(zhuǎn)為utf-8編碼輸出 ????????????????????if?(is_object($val))?{ ????????????????????????$rowData[$currentColumn]?=?''; ????????????????????????foreach?($val->getRichTextElements()?as?$cell)?{ ????????????????????????????$rowData[$currentColumn]?.=?$cell->getText(); ????????????????????????} ????????????????????}?else?{ ????????????????????????$rowData[$currentColumn]?=?$val; ????????????????????} ????????????????????if?(empty($rowData[$currentColumn]))?{ ????????????????????????$blankCell++; ????????????????????} ????????????????} ????????????????if?(!$blankRowDel?||?chr($blankCell?+?64)?!=?$allColumn)?{ ????????????????????$sheetData[]?=?$rowData; ????????????????} ????????????} ????????????$excelData[$currentSheetTitle]?=?array( ????????????????'header'??=>?$colunmNameArray, ????????????????'content'?=>?$sheetData, ????????????); ????????} ????????return?$excelData; ????}
到此這篇關(guān)于PHP內(nèi)存溢出的解決方法詳解的文章就介紹到這了,更多相關(guān)PHP內(nèi)存溢出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
php常用字符串輸出方法分析(echo,print,printf及sprintf)
這篇文章主要介紹了php常用字符串輸出方法,結(jié)合實(shí)例形式分析了echo、print、printf及sprintf輸出字符串的具體用法與相關(guān)使用技巧,需要的朋友可以參考下2016-07-07PHP 正則表達(dá)式之正則處理函數(shù)小結(jié)(preg_match,preg_match_all,preg_replace,pr
本節(jié)我們就來介紹一下PHP中基于perl的正則表達(dá)式處理函數(shù),主要包含了分割, 匹配,查找,替換等等處理操作,依舊是配合示例講解,讓我們開始吧2012-10-10PHP中set_include_path()函數(shù)相關(guān)用法分析
這篇文章主要介紹了PHP中set_include_path()函數(shù)相關(guān)用法,結(jié)合實(shí)例形式分析了php針對文件路徑設(shè)置的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07PHP中cookie知識點(diǎn)學(xué)習(xí)
我們給大家總結(jié)了PHP中cookie的詳細(xì)用法以及重要知識點(diǎn),對此有興趣的朋友可以參考學(xué)習(xí)下。2018-05-05