在PHP中,unset函數是用來清除變量,但在某些狀況unset的行爲會由於要銷燬的變量的類型而有所不一樣。 咱們都知道PHP是動態的弱類型語言,並且PHP的寫機制會使用內存處理的引用計數的複本,PHP中變量主要 有符號表中的條目和實際的變量容器,變量容器以下定義:其中refcount__gc和is_ref__gc分別表示此變 量容器的引用次數和是否被引用。
typedef struct _zval_struct zval;
typedef union _zvalue_value { long lval; /* long value */
double dval; /* double value */ struct { /* string type */ char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; };
下面咱們使用Xdebug查看當某個變量被賦值經過引用時,refcount__gc與is_ref__gc在變量容器中的狀況。
測試代碼<?php $a = 'me';
xdebug_debug_zval('a');
$b = &$a;
xdebug_debug_zval('a');
xdebug_debug_zval('a');
$b = 'too';
var_dump($a);
unset($a);
xdebug_debug_zval('b');?> 程序輸出的狀況以下:
a: (refcount=1, is_ref=0)='me' a: (refcount=2, is_ref=1)='me' b: (refcount=2, is_ref=1)='me' string(3) "too" b: (refcount=1, is_ref=0)='too' 從上面的輸出中,能夠知道變量a,b共用同一個變量容器,當調用unset 銷燬a時,只是改變變量容器的引用計數器,並無 實際清除變量容器,而是取消相應的符號表條目與該變量映射並清除。
下面再看一個函數內部調用unset消費全局變量的情景,測試代碼以下:
<?php function test() { global $a; xdebug_debug_zval('a'); unset($a); xdebug_debug_zval('a'); } $a = 'me'; xdebug_debug_zval('a'); test(); var_dump($a); xdebug_debug_zval('a'); ?> 運行輸出的結果以下:
a: (refcount=1, is_ref=0)='me' a: (refcount=2, is_ref=1)='me' a: (refcount=1, is_ref=0)='me' string(2) "me" a: (refcount=1, is_ref=0)='me' 由上面的結果能夠知道,當在函數內部引用全局變量時,會使全局變量對應的變量容器的引用計數器加一同時說明此變量被引用 過,相似與經過引用傳遞的變量情景,當在函數內部使用unset 銷燬全局變量僅是將相應的引用計數器值以及是否引用作改變。 有時我也在想,當在函數內部調用所有變量會不會臨時在符號表中添加新的一條目,符號標誌也是' a', 只不過做用域是函數 內部,具體的狀況還沒來得及細看,有機會繼續深刻。
從上能夠知道 unset 在撤銷變量時,會對變量對應的變量容器操做, 同時撤銷掉相應的符號表中的條目,至於內存是否 被銷燬,需相應的計數器值爲零,我將第一個例子的代碼後面又添加三行代碼, unset($b); echo 'over'; xdebug_debug_zval('b');
當程序運行後結果以下:
a: (refcount=1, is_ref=0)='me' a: (refcount=2, is_ref=1)='me' b: (refcount=2, is_ref=1)='me' string(3) "too" b: (refcount=1, is_ref=0)='too' over 由結果能夠知道,當對變量b再使用unse時,就會使變量容器被銷燬掉,既內存的值被清除掉,相應的內存塊也被釋放。