搜尋
首頁後端開發php教程PHP垃圾回收機制—引用計數的基本知識

PHP垃圾回收機制—引用計數的基本知識

Nov 22, 2016 am 10:02 AM
phpPHP垃圾回收

每個php變數存在一個叫做"zval"的變數容器中。一個zval變數容器,除了包含變數的類型和值,還包括兩個位元組的額外資訊。第一個是"is_ref",是個bool值,用來識別這個變數是否是屬於引用集合(reference set)。透過這個位元組,php引擎才能把普通變數和引用變數區分開來,由於php允許使用者透過使用&來使用自訂引用,zval變數容器中還有一個內部引用計數機制,來優化記憶體使用。第二個額外位元組是"refcount",用以表示指向這個zval變數容器的變數(也稱為符號即symbol)個數。所有的符號存在一個符號表中,其中每個符號都有作用域(scope),那些主腳本(例如:透過瀏覽器請求的的腳本)和每個函數或方法也都有作用域。

當一個變數被賦化常數值時,就會產生一個zval變數容器,如下例這樣:

Example #1 建立一個新的zval容器

<?php
    $a = "new string";
?>

在上例中,新的變數a,是在當前作用域中產生的。並且產生了類型為 string 和值為new string的變數容器。在額外的兩個位元組資訊中,"is_ref"被預設為 FALSE,因為沒有任何自訂的引用產生。 "refcount" 設定為 1,因為這裡只有一個變數使用這個變數容器. 注意到當"refcount"的值是1時,"is_ref"的值總是FALSE. 如果你已經安裝了Xdebug,你能通過呼叫函數 xdebug_debug_zval()顯示"refcount"和"is_ref"的值。

Example #2 顯示zval訊息

<?php
    xdebug_debug_zval(&#39;a&#39;);
?>

以上程式會輸出:

a: (refcount=1, is_ref=0)=&#39;new string&#39;

把一個變數賦值給另一個變數會增加引用次數(refcount).

Example #3 zval中refcount的成長次數(refcount).

Example #3 zval中refcount的成長

例程會輸出:

<?php
    $a = "new string";
    $b = $a;
    xdebug_debug_zval( &#39;a&#39; );
?>

這時,引用次數是2,因為同一個變數容器被變數 a 和變數 b關聯.當沒必要時,php不會去複製已產生的變數容器。變數容器在」refcount「變成0時就被銷毀. 當任何關聯到某個變數容器的變數離開它的作用域(例如:函數執行結束),或是對變數呼叫了函數 unset()時,」refcount 「就會減1,下面的例子就能說明:

Example #4 zval中refcount的減少

a: (refcount=2, is_ref=0)=&#39;new string&#39;

以上例程會輸出:

<?php
    $a = "new string";
    $c = $b = $a;
    xdebug_debug_zval( &#39;a&#39; );
    unset( $b, $c );
    xdebug_debug_zval( &#39;a&#39; );
?>

如果我們現在執行 unset($a);,包含類型和值的這個變數容器就會從記憶體中刪除。

複合類型(Compound Types)

當考慮像 array和object這樣的複合類型時,事情就稍微有點複雜。與 標量(scalar)類型的值不同,array和 object類型的變數把它們的成員或屬性存在自己的符號表中。這意味著下面的例子將產生三個zval變數容器。

Example #5 建立一個陣列zval

a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)=&#39;new string&#39;

以上程式的輸出類似:

<?php
    $a = array( &#39;meaning&#39; => &#39;life&#39;, &#39;number&#39; => 42 );
    xdebug_debug_zval( &#39;a&#39; );
?>

Example #6 增加一個已存在的元素到陣列中

a: (refcount=1, is_ref=0)=array (
   &#39;meaning&#39; => (refcount=1, is_ref=0)=&#39;life&#39;,
   &#39;number&#39; => (refcount=1, is_ref=0)=42
)

以上程式的輸出類似於:

rerereee

圖形化顯示如下:

 從以上的xdebug輸出訊息,我們看到原有的陣列元素和新加入的陣列元素關聯到同一個"refcount"2的zval變數容器. 儘管Xdebug的輸出顯示兩個值為'life'的zval 變數容器,其實是同一個。 函數 xdebug_debug_zval()不顯示這個訊息,但是你能透過顯示記憶體指標資訊來看到。

刪除數組中的一個元素,就是類似於從作用域中刪除一個變數. 刪除後,數組中的這個元素所在的容器的“refcount”值減少,同樣,當“refcount”為0時,這個變數容器就從記憶體中被刪除,下面又一個例子可以說明:

Example #7 從數組中移除元素

<?php
    $a = array( &#39;meaning&#39; => &#39;life&#39;, &#39;number&#39; => 42 );
    $a[&#39;life&#39;] = $a[&#39;meaning&#39;];
    xdebug_debug_zval( &#39;a&#39; );
?>

以上例程的輸出類似於:

a: (refcount=1, is_ref=0)=array (
   &#39;meaning&#39; => (refcount=2, is_ref=0)=&#39;life&#39;,
   &#39;number&#39; => (refcount=1, is_ref=0)=42,
   &#39;life&#39; => (refcount=2, is_ref=0)=&#39;life&#39;
)

現在,當我們添加一個數組本身作為這個陣列的元素時,事情就變得有趣,下個例子將說明這個。範例中我們加入了引用操作符,否則php將產生一個複製。

Example #8 加入陣列元素到陣列本身

<?php
    $a = array( &#39;meaning&#39; => &#39;life&#39;, &#39;number&#39; => 42 );
    $a[&#39;life&#39;] = $a[&#39;meaning&#39;];
    unset( $a[&#39;meaning&#39;], $a[&#39;number&#39;] );
    xdebug_debug_zval( &#39;a&#39; );
?>

以上程式的輸出類似:

a: (refcount=1, is_ref=0)=array (
   &#39;life&#39; => (refcount=1, is_ref=0)=&#39;life&#39;
)

Or graphically

 能看到陣列變數(a) 同時也是這個陣列的第二個元素(1)指向的變數容器中「refcount」為 2。上面的輸出結果中的"..."說明發生了遞歸操作, 顯然在這種情況下意味著"..."指向原始數組。

跟剛剛一樣,對一個變數呼叫unset,將刪除這個符號,且它指向的變數容器中的引用次數也減1。所以,如果我們在執行上面的程式碼後,對變數$a調用unset, 那麼變數 $a和陣列元素"1" 所指向的變數容器的引用次數減1, 從"2"變成"1".下例可以說明:

Example #9 銷毀 $a

<?php
    $a = array( &#39;one&#39; );
    $a[] =& $a;
    xdebug_debug_zval( &#39;a&#39; );
?>

或圖形化顯示如下:🎜

 清理變數容器的問題(Cleanup Problems)

儘管不再有某個作用域中的任何符號指向這個結構(就是變數容器),由於數組元素「1」仍然指向數組本身,所以這個容器不能被清除。因為沒有另外的符號指向它,使用者沒有辦法清除這個結構,結果就會導致記憶體洩漏。慶幸的是,php將在請求結束時清除這個資料結構,但是在php清除之前,將耗費不少空間的記憶體。如果你要實作分析演算法,或是要做其他像一個子元素指向它的父元素這樣的事情,這種情況就會經常發生。當然,同樣的情況也會發生在物件上,實際上物件更有可能出現這種情況,因為物件總是隱式的被引用。

如果上面的情況發生僅僅一兩次倒沒什麼,但是如果出現幾千次,甚至幾十萬次的內存洩漏,這顯然是個大問題。在長時間運行的腳本,例如請求基本上不會結束的守護程序(deamons)或者單元測試中的大的套件(sets)中,在給eZ 組件庫的模板組件做單元測試時,後者(指單元測試中的大的套件)就會出現問題.它將需要耗用2GB的內存,而一般的測試伺服器沒有這麼大的內存空間。


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
PHP如何識別用戶的會話?PHP如何識別用戶的會話?May 01, 2025 am 12:23 AM

phpIdentifiesauser'ssessionSessionSessionCookiesAndSessionId.1)whiwsession_start()被稱為,phpgeneratesainiquesesesessionIdStoredInacookInAcookInAcienamedInAcienamedphpsessIdontheuser'sbrowser'sbrowser.2)thisIdallowSphptpptpptpptpptpptpptpptoretoreteretrieetrieetrieetrieetrieetrieetreetrieetrieetrieetrieetremthafromtheserver。

確保PHP會議的一些最佳實踐是什麼?確保PHP會議的一些最佳實踐是什麼?May 01, 2025 am 12:22 AM

PHP會話的安全可以通過以下措施實現:1.使用session_regenerate_id()在用戶登錄或重要操作時重新生成會話ID。 2.通過HTTPS協議加密傳輸會話ID。 3.使用session_save_path()指定安全目錄存儲會話數據,並正確設置權限。

PHP會話文件默認存儲在哪裡?PHP會話文件默認存儲在哪裡?May 01, 2025 am 12:15 AM

phpsessionFilesArestoredIntheDirectorySpecifiedBysession.save_path,通常是/tmponunix-likesystemsorc:\ windows \ windows \ temponwindows.tocustomizethis:tocustomizEthis:1)useession_save_save_save_path_path()

您如何從PHP會話中檢索數據?您如何從PHP會話中檢索數據?May 01, 2025 am 12:11 AM

ToretrievedatafromaPHPsession,startthesessionwithsession_start()andaccessvariablesinthe$_SESSIONarray.Forexample:1)Startthesession:session_start().2)Retrievedata:$username=$_SESSION['username'];echo"Welcome,".$username;.Sessionsareserver-si

您如何使用會議來實施購物車?您如何使用會議來實施購物車?May 01, 2025 am 12:10 AM

利用會話構建高效購物車系統的步驟包括:1)理解會話的定義與作用,會話是服務器端的存儲機制,用於跨請求維護用戶狀態;2)實現基本的會話管理,如添加商品到購物車;3)擴展到高級用法,支持商品數量管理和刪除;4)優化性能和安全性,通過持久化會話數據和使用安全的會話標識符。

您如何在PHP中創建和使用接口?您如何在PHP中創建和使用接口?Apr 30, 2025 pm 03:40 PM

本文解釋瞭如何創建,實施和使用PHP中的接口,重點關注其對代碼組織和可維護性的好處。

crypt()和password_hash()有什麼區別?crypt()和password_hash()有什麼區別?Apr 30, 2025 pm 03:39 PM

本文討論了PHP中的crypt()和password_hash()的差異,以進行密碼哈希,重點介紹其實施,安全性和對現代Web應用程序的適用性。

如何防止PHP中的跨站點腳本(XSS)?如何防止PHP中的跨站點腳本(XSS)?Apr 30, 2025 pm 03:38 PM

文章討論了通過輸入驗證,輸出編碼以及使用OWASP ESAPI和HTML淨化器之類的工具來防止PHP中的跨站點腳本(XSS)。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3 Mac版

SublimeText3 Mac版

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

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。