PHP 內存中的對象和引用簡介

已發表: 2022-03-11

為了更好地理解 PHP 如何管理內存中的變量和對象,我在學習 PHP 認證時首先起草了這篇文章。 經過大量研究,我意識到要找到我的問題的答案並不容易,所以一旦我完成了,我決定將這些信息記錄下來,以便人們可以在一個地方找到它們。

在本文中,我將討論如何在內存中控制對象和變量引用,因為這是一個可以引發討論和不同意見的問題。 需要思考的一個問題是:“默認情況下,對像在 PHP 中是通過引用傳遞還是通過複製傳遞?” 我將首先討論 PHP 中沒有哪些引用; 其次,我將討論它們什麼,最後,我將研究垃圾收集器在 PHP 中是如何工作的。

執行$a = new Foo();類的語句時,PHP 如何在內存中創建對象? 如今,內存不再像過去那樣昂貴且資源有限。 但是,對於優秀的 PHP 開發人員來說,了解和理解在執行應用程序期間如何在內部管理變量和對象仍然很重要。

PHP 內存和 PHP 垃圾回收中的對象和引用

PHP 中的對象和引用

許多人說——在 PHP 書籍和網上——默認情況下 PHP 中的對像是通過引用傳遞的。 其他人說 PHP 中的對像是通過副本分配的。 要弄清楚哪個陳述是正確的,首先我們必須分析 PHP 中什麼是(什麼不是)引用。

什麼不是PHP 中的引用?

比知道 PHP 中的引用是什麼更重要的是知道它們不是什麼。 在 PHP 中,引用不是 C 風格的指針; 您不能像使用 C 指針那樣對引用進行算術運算。 為什麼? 因為,與 C 不同,PHP 引用並不是真正的內存地址,因為它們不是表示內存位置的數字。 但是,什麼參考?

PHP 中的引用是什麼

在 PHP 中,引用是允許兩個不同變量讀取和寫入單個值的“別名”。 換句話說,它們是允許從具有不同名稱的變量訪問相同值的機制,因此它們的行為就像它們是同一個變量一樣。 請記住,在 PHP 中,變量名和變量的內容是兩個完全不同的東西,在所謂的“符號表”中鏈接。 因此,當我們創建一個引用時,它只是在符號表中為該變量添加一個別名。 假設我們有以下代碼:

 $a = new Foo();

執行上述語句時,在內存中創建變量$a ,在內存中創建Foo類型的對象,並在符號表中添加一個條目,表明變量$a “引用”(或與, 或指向, 或任何你想調用的對象) Foo對象, 但它本身並不是指向該對象的指針。 從概念上講,我們有這樣的插圖:

PHP 內存中的對象引用

小測驗:如果我們執行這個會發生什麼?

 $b = $a;

並不是$b成為$a a 的引用; 我們也不能說$b$a a 的副本。 真正發生的是,我們在內存中創建了一個新變量$b ,然後在符號表中添加了一個新條目,表明變量$b也引用了與$a a 相同的Foo對象。 因此,從視覺上看,我們有一些類似於此插圖中顯示的內容:

PHP 內存中的對象引用

現在,如果我們執行:

 $c = &$a;

我們將在內存中創建第三個變量$c ,但不會在符號表中為$c創建新條目。 相反,在符號表中,記錄了$c$a a 的別名,因此它的行為相同,但$c不是指向$a的指針——這與 C 中不同,它創建了稱為指針指針的東西。 為了形象化,我們有一些類似於下圖所示的東西:

變量及其別名圖

一旦我們想要修改這三個變量中的任何一個的值(即寫入一個新值),PHP 就必須在內存中創建一個新的z_val結構來分隔變量$b的內容和對$a / $c因此它們可以各自獨立修改而不會影響對方的價值。 因此,如果我們在前面的腳本中添加以下行:

 $b = new Bar();

在記憶中,我們會遇到如下圖所示的情況:

上述情況的圖形表示

現在,讓我們考慮一個更完整的例子:

 <?php class myClass { public $var; function __construct() { $this->var = 1; } function inc() { return ++$this->var; } } $a = new myClass(); // $a "references" a Foo object $b = $a; //b also references the same Foo object as a //($a) == ($b) == <id> of Foo object, but a and b are different entries in symbols table echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); $c = &$a; //$c is an alias of $a //($a, $c) == <id> of Foo object, c is an alias of a in the symbols table echo "$c = ";var_dump($c); $a = NULL; //The entry in the symbols table which links "$a" with Foo object is removed //Since that entry was removed, $c is not related to Foo anymore //Anyway, Foo still exists in memory and it is still linked by $b echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); echo "$c = ";var_dump($c); echo "$b->var: ".$b->inc(); echo "$b->var: ".$b->inc(); $b = NULL; //The entry in the symbols table which links "$b" with the Foo object is removed //There are no more entries in the symbols table linked to Foo, //So, Foo is not referenced anymore and can be deleted by the garbage collector echo "$b = ";var_dump($b);

執行上述腳本產生的輸出是:

 $a = object(myClass)#1 (1) { ["var"]=> int(1) } $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = object(myClass)#1 (1) { ["var"]=> int(1) } $a = NULL $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = NULL $b->var: 2 $b->var: 3 $b = NULL

PHP 垃圾回收

最後,讓我們看看 PHP 垃圾回收是如何工作的,因為它是在 5.3 版本中引入的。 當符號表中沒有對該對象的引用時,PHP 垃圾收集器將刪除 PHP 內存中的對像或變量。 也就是說,PHP 從對象創建之時起就維護一個對象的引用計數器,以便在 PHP 腳本執行期間,計數器根據“指向”它的變量來遞增和遞減引用計數器。 一旦引用計數達到 0(即,沒有引用該對象,因此它沒有被使用),PHP 將該對象標記為可移除,以便在 PHP 垃圾收集器的下一次傳遞中,它將從內存中移除,釋放該空間以供重用。 如果您想更深入地了解 PHP 垃圾回收的工作原理,請閱讀此文檔。

結束的想法

我希望我已經澄清了一點,PHP 如何處理內存中的對象和變量,以及它如何“選擇”應該由 PHP 垃圾收集器刪除的對象。

既然您了解了 PHP 如何在內部管理內存中的變量和對象,請拿起您的筆記本電腦並開始嘗試一些代碼來證明您所學的知識。 嘗試使用變量和引用。 此外,嘗試更改變量的值如何影響引用它的另一個變量的值。 給你一個問題:執行下面的代碼後, $a$b的值是多少?

 $a = '1'; $b = &$a; $b = "2$b";

如果您有興趣閱讀有關 PHP 性能特性的更多信息,請查看 Toptaler Vilson Duka 的這篇文章。