Введение в объекты и ссылки в памяти PHP

Опубликовано: 2022-03-11

Впервые я написал эту статью во время подготовки к сертификации по PHP, чтобы лучше понять, как PHP управляет переменными и объектами в памяти. После долгих исследований я понял, что найти ответы на свои вопросы было непросто, поэтому, когда я закончил, я решил задокументировать информацию, чтобы люди могли найти ее всю в одном месте.

В этой статье я расскажу о том, как в памяти контролируются ссылки на объекты и переменные, так как это вопрос, который может вызвать обсуждение и разные мнения. Один вопрос для размышления: «По умолчанию объекты передаются по ссылке или путем копирования в PHP?» Сначала я расскажу о том, чего нет в PHP; во-вторых, я расскажу, что они из себя представляют , и, наконец, я рассмотрю, как работает сборщик мусора в PHP.

Как PHP создает объекты в памяти при выполнении такого оператора, как $a = new Foo(); ? В настоящее время память не является таким дорогим и ограниченным ресурсом, как это было в прошлом. Тем не менее, для хороших 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 ; мы также не можем сказать, что $b является копией $a . На самом деле произошло то, что мы создали новую переменную $b в памяти, а затем добавили новую запись в таблицу символов, указывающую, что переменная $b также ссылается на тот же объект Foo , что $a . Итак, визуально мы имеем нечто похожее на то, что показано на этой иллюстрации:

Ссылки на объекты в памяти PHP

Теперь, если мы выполним:

 $c = &$a;

Мы создадим третью переменную $c в памяти, но не новую запись в таблице символов для $c . Вместо этого в таблице символов записано, что $c является псевдонимом для $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 Вилсона Дуки.