最近有人跟我反映主機上一個wordpress外掛程式的使用過程中經常會出現錯誤,開啟了php的dispaly_error功能之後,發現其報“Fatal error: Allowed memory size of 35389440 bytes exhausted(tried to allocate 1406507 bytes) in xxxxxx on line xxx”,意思是致命錯誤,記憶體被耗盡了。Google上搜尋了一下,網上有很多這樣的問題,答案也都一樣,更改php的記憶體限制,把php.ini中的memory_limit改為更大的數值。
但是這裡有一個很大的問題,php允許訪問的記憶體大小是35389440位元組,而實際要分配的只有1406507位元組。為什麼要分配的記憶體比實際允許訪問的記憶體小還會引發致命錯誤呢?
查了很多這方面的資料,發現很多這樣搜尋結果,都是實際要分配的記憶體要小於允許的記憶體,但是沒有找到對此的說明。
首先想到是不是PHP本身這個記憶體限制功能有bug,於是做了如下實驗:
1, 產生一個10MB大小的檔案 dd if=/dev/zero of=10mb bs=1M count=10
2, 產生如下php檔案並訪問:
發現其報錯“Fatal error: Allowed memory size of 2097152 bytes exhausted (tried to allocate 10493952 bytes)”,這個報錯和我們設定的2M的記憶體限制,實際要分配10M的情況是一樣的。這說明記憶體限制本身功能是沒有問題的,報錯的資料和實際的資料是能對上號的。
那麼,上面的問題出在哪裡呢?為什麼實際要分配的記憶體比限制的記憶體要小,還會導致出錯?又查了一下php關於memory_limit的說明:
This sets the maximum amount of memory in bytes that a script is allowed to allocate. This helps prevent poorly written scripts for eating up all available memory on a server. Note that to have no memory limit, set this directive to -1.
這裡也沒有對上述問題進行說明,只是告訴我們memory_limit用來限制一個指令碼中分配的記憶體大小用來保護伺服器免受poorly written scripts的影響。也就是說memory_limit是限制單個PHP運行指令碼的可訪問記憶體大小的,那會不會存在一種可能,整個線程中的多個操作加起來的記憶體超過了總限制大小,但單個操作的記憶體又只佔整個限制的很小一部分,而最後那根壓死駱駝的稻草是PHP報錯出來的,所以顯示出試圖訪問記憶體小於限制的記憶體大小?為了驗證這個猜想,我把上述PHP檔案改成這樣並訪問:
發現其報錯“Fatal error: Allowed memory size of 15728640 bytes exhausted (tried to allocate 10493952 bytes)”,實際要分配10M記憶體,小於允許訪問的15M,驗證了前面的猜想。也就是說, 程式在運行時如果超過設定的記憶體,PHP報錯資訊只會提示當前操作試圖訪問的記憶體大小。
那麼直接增加記憶體限制的大小就可以解決這個問題了,限制的大小可以通過在php的shutdown函數上註冊memory_get_usage函數來統計網站的記憶體消耗,取一個合適的值即可。
參考資料
-
memory limit