一、前言
這篇文章其實是上篇文章的內存優化部分。博主的php程序在執行的時候,報錯:
Out of memory (allocated 364904448) (tried to allocate 262144 bytes)
也就是傳統的內存不足報錯,問題是我本地設置的內存已經是1280M了,簡直不能忍。因此這里一步步的看一篇代碼,記錄一下需要優化的地方,主要是針對數組的
二、優化前准備
1、首先是查看php的當前內存設置
windows: 打開php.ini,搜索:memory_limit ,一般設置為128M夠用
linux: 執行“php -i | grep Loaded Configuration File”來找到對應的配置文件。
注: 這個命令是搜索php程序的配置文件所在位置,打開phpinfo,可以看到有個Loaded Configuration File 選項,對應
的就是php.ini文件的位置
2、一個php數組能占用多大內存
參考:https://blog.csdn.net/hguisu/article/details/7376705
通過大佬的分析,我們可以知道:
(1)php對於數組的利用效率很低,一個在C語言里面100M 內存的數組,在PHP里面就要1G;
(2)php空數組都要14(zval) + 39(HashTable) + 33(arBuckets) = 86 個字節
(3)php查看內存方法:memory_get_usage() ,具體用法: echo memory_get_usage(); 即可,博主通過該方法,確認內存瓶頸出在了數組部分
三、優化過程
1、代碼冗余
$arr = [];
foreach($response['hits']['hits'] as $v)
{
$arr[] = $v['_source'];
}
unset($v);
//加上原來沒有的一些字段,篩選掉沒有pixel的數據
foreach($arr as $key=>$v){
if(!array_key_exists('pixel.uuid',$v)){
unset($arr[$key]);
}
}
優化點評: 這里的$arr是完全不必要的,既然下面還是要篩選,那么直接循環下面的那個$response['hits']['hits']即可,如果$arr是很大的數組,那么在賦值之后,又要開辟一塊內存給它。所以要盡量避免這種情況的發生。
2、數組賦值給另一個空數組
if(count($this->arrEsIndex) > 0){
unset($this->arrEsIndex);
$this->arrEsIndex = $arr;
}else{
$this->arrEsIndex = $arr;
}
優化點評: 像這種數組賦值操作盡量少做。因為把 $arr賦值給$this->arrEsindex之后,如果$this->arrEsIndex的值改變了,那么使用的內存相當於翻倍的效果。其次是賦值之后,這個$arr其實已經沒用了,但是由於咱們沒有進行unset,所以就造成這個$arr還在占用內存的情況。建議是unset($arr),也就是unset掉咱們不用的那些數組。
正常的賦值是不會發生內存改變的,但是當賦值的新數組發生改動的時候,php就會新開辟內存給新的數組,這里會造成無謂的內存消耗。最好是不要直接這樣賦值,如果非賦值不可的話,記得加上‘&’符號,通過傳引用直接傳遞地址給新數組,這樣當新數組發生變化的時候,更改的還是原來的那塊內存。
3、把數組傳參給函數
$this->getScrollData($repos);
優化點評: 這里的$repos是一個數組。正常來說,傳值傳數組也是可以的,但是如果這個數組里面的元素是萬級別的,那么
這個操作也是非常耗內存的。在php程序中,只要傳參,參數都會拷貝一份,所以值越大,耗的內存越大。針對這種情況,
建議是在類里面定義全局變量,然后函數體里面通過:$this->repos來操作這個數組。也可以考慮使用傳引用的方式,因為使用&的話,傳遞過去的事一個內存地址,位數並不大。
4、把判斷條件寫在循環外面,避免每次都要循環的情況
foreach($this->arrEsIndex as $k=>&$v) {
if (!empty($this->search_abtest_key) && $this->search_abtest_key != "is_50mclient") {
$v[$this->search_abtest_key] = 0;
}
}
優化點評: 這個操作也有問題,如果if條件滿足的話還好。如果If條件不滿足的話,在業務層面根本就不需要進行這部分操作。但是由於咱們的forteach循環在最外面,所以照樣會循環一下數組。問題這是個很大的數組,消耗的內存也很可觀。
5、unset掉比較大的變量
針對一些比較大的變量(最好大於256字節),如果只是臨時使用的話,使用完之后記得unset()掉。如果對變量進行'&'傳引用的話,會增加該內存的引用計數,直接unset()變量並不會立馬釋放變量,因為unset只是斷開一個變量到一塊內存區域的連接,同時將該內存區域的引用計數-1,如果把引用賦值的那個變量也unset()掉才會立馬釋放內存。
6、單個大數組消耗太多內存的解決方案
參考:http://www.phpzy.com/php/16958.html
通過存入字符串的方式優化,使用數組的時候就再把字符串轉化為數組
7、循環操作DB
這個問題是老生常談了,博主代碼里沒有出現這個情況,不過大家還是注意下比較好。循環不斷操作DB,非常影響程序性能。
四、總結
以上的優化部分也是博主一步步看着代碼慢慢優化的,相對而言都是一些比較淺層面的優化。不過這些問題也告訴我們,寫完代碼記得要再看一遍,如果可以的話,在開始編寫代碼的時候就要注意性能問題。最差勁也要實現業務之后,重新看一遍代碼,優化代碼結構,釋放掉不必要的變量,改掉太消耗內存的操作。
博主這里優化之后,原來的代碼1280M的內存都不夠用,現在128M的內存妥妥的。程序執行下來,總共占用內存10M左右,還是比較合適的。如果大家也出現內存不足的情況,那么除了增大本身的php內存之外,也要考慮優化下程序哦。