淺談PHP 5中記憶體回收演算法的演化_PHP教程

來源:互聯網
上載者:User
PHP是一門託管型語言,在PHP編程中程式員不需要手工處理記憶體資源的分配與釋放(使用C編寫PHP或Zend擴充除外),這就意味著PHP本身實現了記憶體回收機制(Garbage Collection)。現在如果去PHP官方網站(php.net)可以看到,目前PHP5的兩個分支版本PHP5.2和PHP5.3是分別更新的,這是因為許多項目仍然使用5.2版本的PHP,而5.3版本對5.2並不是完全相容。PHP5.3在PHP5.2的基礎上做了諸多改進,其中記憶體回收演算法就屬於一個比較大的改變。本文將分別討論PHP5.2和PHP5.3的記憶體回收機制,並討論這種演化和改進對於程式員編寫PHP的影響以及要注意的問題。

PHP變數及關聯記憶體對象的內部表示

記憶體回收說到底是對變數及其所關聯記憶體對象的操作,所以在討論PHP的記憶體回收機制之前,先簡要介紹PHP中變數及其記憶體對象的內部表示(其C原始碼中的表示)。

PHP官方文檔中將PHP中的變數劃分為兩類:標量類型和複雜類型。標量類型包括布爾型、整型、浮點型和字串;複雜類型包括數組、對象和資源;還有一個NULL比較特殊,它不劃分為任何類型,而是單獨成為一類。

所有這些類型,在PHP內部統一用一個叫做zval的結構表示,在PHP原始碼中這個結構名稱為“_zval_struct”。zval的具體定義在PHP原始碼的“Zend/zend.h”檔案中,下面是相關代碼的摘錄。

 
  1. typedef union _zvalue_value {
  2. long lval; /* long value */
  3. double dval; /* double value */
  4. struct {
  5. char *val;
  6. int len;
  7. } str;
  8. HashTable *ht; /* hash table value */
  9. zend_object_value obj;
  10. } zvalue_value;
  11. struct _zval_struct {
  12. /* Variable information */
  13. zvalue_value value;
  14. /* value */
  15. zend_uint refcount__gc;
  16. zend_uchar type; /* active type */
  17. zend_uchar is_ref__gc;
  18. };

其中聯合體“_zvalue_value”用於表示PHP中所有變數的值,這裡之所以使用union,是因為一個zval在一個時刻只能表示一種類型的變數。可以看到_zvalue_value中只有5個欄位,但是PHP中算上NULL有8種資料類型,那麼PHP內部是如何用5個欄位表示8種類型呢?這算是PHP設計比較巧妙的一個地方,它通過複用欄位達到了減少欄位的目的。例如,在PHP內部布爾型、整型及資源(只要儲存資源的標識符即可)都是通過lval欄位儲存的;dval用於儲存浮點型;str儲存字串;ht儲存數組(注意PHP中的數組其實是雜湊表);而obj儲存物件類型;如果所有欄位全部置為0或NULL則表示PHP中的NULL,這樣就達到了用5個欄位儲存8種類型的值。

而當前zval中的value(value的類型即是_zvalue_value)到底表示那種類型,則由“_zval_struct”中的type確定。_zval_struct即是zval在C語言中的具體實現,每個zval表示一個變數的記憶體對象。除了value和type,可以看到_zval_struct中還有兩個欄位refcount__gc和is_ref__gc,從其尾碼就可以斷定這兩個傢伙與記憶體回收有關。沒錯,PHP的記憶體回收全靠這倆欄位了。其中refcount__gc表示當前有幾個變數引用此zval,而is_ref__gc表示當前zval是否被按引用引用,這話聽起來很拗口,這和PHP中zval的“Write-On-Copy”機制有關,由於這個話題不是本文重點,因此這裡不再詳述,讀者只需記住refcount__gc這個欄位的作用即可。

PHP5.2中的記憶體回收演算法——Reference Counting

PHP5.2中使用的記憶體回收演算法是大名鼎鼎的Reference Counting,這個演算法中文翻譯叫做“引用計數”,其思想非常直觀和簡潔:為每個記憶體對象分配一個計數器,當一個記憶體對象建立時計數器初始化為1(因此此時總是有一個變數引用此對象),以後每有一個新變數引用此記憶體對象,則計數器加1,而每當減少一個引用此記憶體對象的變數則計數器減1,當記憶體回收機制運作的時候,將所有計數器為0的記憶體對象銷毀並回收其佔用的記憶體。而PHP中記憶體對象就是zval,而計數器就是refcount__gc。

例如下面一段PHP代碼示範了PHP5.2計數器的工作原理(計數器值通過xdebug得到):

 
  1. $val1 = 100; //zval(val1).refcount_gc = 1;
  2. $val2 = $val1; //zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(因為是Write on copy,當前val2與val1共同引用一個zval)
  3. $val2 = 200; //zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此處val2建立了一個zval)
  4. unset($val1); //zval(val1).refcount_gc = 0($val1引用的zval再也不可用,會被GC回收)
  5. ?>

Reference Counting簡單直觀,實現方便,但卻存在一個致命的缺陷,就是容易造成記憶體泄露。很多朋友可能已經意識到了,如果存在循環參考,那麼Reference Counting就可能導致記憶體泄露。例如下面的代碼:

 
  1. $a = array();
  2. $a[] = & $a;
  3. unset($a);
  4. ?>

這段代碼首先建立了數組a,然後讓a的第一個元素按引用指向a,這時a的zval的refcount就變為2,然後我們銷毀變數a,此時a最初指向的zval的refcount為1,但是我們再也沒有辦法對其進行操作,因為其形成了一個迴圈自引用,如所示:


http://www.bkjia.com/PHPjc/445838.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/445838.htmlTechArticlePHP是一門託管型語言,在PHP編程中程式員不需要手工處理記憶體資源的分配與釋放(使用C編寫PHP或Zend擴充除外),這就意味著PHP本身實現了垃圾...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.