淺談PHP 5中垃圾回收演算法的演化_PHP教程
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」檔案中,以下是相關程式碼的摘錄。
<ol class="dp-c"> <li class="alt"><span><span>typedef union _zvalue_value { </span></span></li> <li> <span> long lval; </span><span class="comment">/* long value */</span><span> </span> </li> <li class="alt"> <span> double dval; </span><span class="comment">/* double value */</span><span> </span> </li> <li><span> struct { </span></li> <li class="alt"><span> char *val; </span></li> <li><span> int len; </span></li> <li class="alt"><span> } str; </span></li> <li> <span> HashTable *ht; </span><span class="comment">/* hash table value */</span><span> </span> </li> <li class="alt"><span> zend_object_value obj; </span></li> <li><span>} zvalue_value; </span></li> <li class="alt"><span> </span></li> <li><span>struct _zval_struct { </span></li> <li class="alt"> <span> </span><span class="comment">/* Variable information */</span><span> </span> </li> <li><span> zvalue_value value; </span></li> <li class="alt"> <span class="comment">/* value */</span><span> </span> </li> <li><span> zend_uint refcount__gc; </span></li> <li class="alt"> <span> zend_uchar type; </span><span class="comment">/* active type */</span><span> </span> </li> <li><span> zend_uchar is_ref__gc; </span></li> <li class="alt"><span>}; </span></li> </ol>
其中聯合體「_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得到):
<ol class="dp-c"><li class="alt"><span><span><?php </span></span></li><li><span> </span></li><li class="alt"><span class="vars">$val1</span><span> = 100; </span><span class="comment">//zval(val1).refcount_gc = 1; </span><span> </span></li><li><span class="vars">$val2</span><span> = </span><span class="vars">$val1</span><span>; </span><span class="comment">//zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(因为是Write on copy,当前val2与val1共同引用一个zval) </span><span> </span></li><li class="alt"><span class="vars">$val2</span><span> = 200; </span><span class="comment">//zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此处val2新建了一个zval) </span><span> </span></li><li><span>unset(</span><span class="vars">$val1</span><span>); </span><span class="comment">//zval(val1).refcount_gc = 0($val1引用的zval再也不可用,会被GC回收) </span><span> </span></li><li class="alt"><span> </span></li><li><span>?> </span></span></li></ol>
Reference Counting簡單直觀,實現方便,但卻存在一個致命的缺陷,就是容易造成記憶體外洩。很多朋友可能已經意識到了,如果存在循環引用,那麼Reference Counting就可能導致記憶體外洩。例如下面的程式碼:
<ol class="dp-c"><li class="alt"><span><span><?php </span></span></li><li><span> </span></li><li class="alt"><span class="vars">$a</span><span> = </span><span class="keyword">array</span><span>(); </span></li><li><span class="vars">$a</span><span>[] = & </span><span class="vars">$a</span><span>; </span></li><li class="alt"><span>unset(</span><span class="vars">$a</span><span>); </span></li><li><span> </span></li><li class="alt"><span>?> </span></span></li></ol>
這段程式碼先建立了陣列a,然後讓a的第一個元素按引用指向a,這時a的zval的refcount就變成2,然後我們銷毀變數a,此時a最初指向的zval的refcount為1,但是我們再也沒有辦法對其進行操作,因為其形成了一個循環自引用,如下圖所示:

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。
