轉載於Laruence大神的部落格
原文地址: http://www.laruence.com/2011/03/04/1894.html
04 Mar 11 深入理解PHP記憶體管理之誰動了我的記憶體 作者: Laruence( ) 本文地址: http://www.laruence.com/2011/03/04/1894.html 轉載請註明出處
首先讓我們看一個問題: 如下代碼的輸出,
var_dump(memory_get_usage()); $a = "laruence"; var_dump(memory_get_usage()); unset($a); var_dump(memory_get_usage());
輸出(在我的個人電腦上, 可能會因為系統,PHP版本,載入的擴充不同而不同):
int(90440) int(90640) int(90472)
注意到 90472-90440=32, 於是就有了各種的結論, 有的人說PHP的unset並不真正釋放記憶體, 有的說, PHP的unset只是在釋放大變數(大量字串, 大數組)的時候才會真正free記憶體, 更有人說, 在PHP層面討論記憶體是沒有意義的.
那麼, 到底unset會不會釋放記憶體? 這32個位元組跑哪裡去了?
要回答這個問題, 我將從倆個方面入手: 這32個位元組去哪裡了
首先我們要打破一個思維: PHP不像C語言那樣, 只有你顯示的調用記憶體配置相關API才會有記憶體的分配.
也就是說, 在PHP中, 有很多我們看不到的記憶體配置過程.
比如對於:
$a = "laruence";
隱式的記憶體配置點就有:
1. 為變數名分配記憶體, 存入符號表 2. 為變數值分配記憶體
所以, 不能只看錶象.
第二, 別懷疑,PHP的unset確實會釋放記憶體(當然, 還要結合引用和計數, 這部分的內容請參看我之前的文章深入理解PHP原理之變數分離/引用), 但這個釋放不是C編程意義上的釋放, 不是交回給OS.
對於PHP來說, 它自身提供了一套和C語言對記憶體配置相似的記憶體管理API:
emalloc(size_t size); efree(void *ptr); ecalloc(size_t nmemb, size_t size); erealloc(void *ptr, size_t size); estrdup(const char *s); estrndup(const char *s, unsigned int length);
這些API和C的API意義對應, 在PHP內部都是通過這些API來管理記憶體的.
當我們調用emalloc申請記憶體的時候, PHP並不是簡單的向OS要記憶體, 而是會像OS要一個大塊的記憶體, 然後把其中的一塊分配給申請者, 這樣當再有邏輯來申請記憶體的時候, 就不再需要向OS申請記憶體了, 避免了頻繁的系統調用.
比如如下的例子:
<?php var_dump(memory_get_usage(TRUE)); //注意擷取的是real_size $a = "laruence"; var_dump(memory_get_usage(TRUE)); unset($a); var_dump(memory_get_usage(TRUE));
輸出:
int(262144) int(262144) int(262144)
也就是我們在定義變數$a的時候, PHP並沒有向系統申請新記憶體.
同樣的, 在我們調用efree釋放記憶體的時候, PHP也不會把記憶體還給OS, 而會把這塊記憶體, 歸入自己維護的空閑記憶體列表. 而對於小塊記憶體來說, 更可能的是, 把它放到記憶體緩衝列表中去(後記, 某些版本的PHP, 比如我驗證過的PHP5.2.4, 5.2.6, 5.2.8, 在調用get_memory_usage()的時候, 不會減去記憶體緩衝列表中的可用記憶體塊大小, 導致看起來, unset以後記憶體不變, 見評論).
現在讓我來回答這32個位元組跑哪裡去了, 就向我剛才說的, 很多記憶體配置的過程不是顯式的, 看了下面的代碼你就明白了:
<?php var_dump("I am Laruence, From http://www.laruence.com"); var_dump(memory_get_usage()); $a = "laruence"; var_dump(memory_get_usage()); unset($a); var_dump(memory_get_usage());