Wprowadzenie do obiektów i referencji w pamięci PHP
Opublikowany: 2022-03-11Po raz pierwszy napisałem ten artykuł podczas studiów do certyfikatu PHP, aby lepiej zrozumieć, jak PHP zarządza zmiennymi i obiektami w pamięci. Po wielu badaniach zdałem sobie sprawę, że nie jest łatwo znaleźć odpowiedzi na moje pytania, więc kiedy skończyłem, postanowiłem udokumentować informacje, aby ludzie mogli je znaleźć w jednym miejscu.
W tym artykule omówię, w jaki sposób odwołania do obiektów i zmiennych są kontrolowane w pamięci, ponieważ jest to kwestia, która może generować dyskusję i różne opinie. Jedno pytanie do rozważenia to: „Domyślnie, czy obiekty są przekazywane przez referencję czy przez kopię w PHP?” Najpierw opowiem o tym, jakich referencji nie ma w PHP; po drugie, omówię czym one są , a na koniec zbadam jak działa garbage collector w PHP.
W jaki sposób PHP tworzy obiekty w pamięci podczas wykonywania instrukcji typu $a = new Foo();
? W dzisiejszych czasach pamięć nie jest tak droga i ma ograniczony zasób, jak w przeszłości. Jednak nadal ważne jest, aby dobrzy programiści PHP wiedzieli i rozumieli, w jaki sposób zmienne i obiekty są zarządzane wewnętrznie podczas wykonywania ich aplikacji.
Obiekty i referencje w PHP
Wiele osób mówi — w książkach o PHP iw Internecie — że obiekty w PHP są domyślnie przekazywane przez referencje. Inni twierdzą, że obiekty w PHP są przydzielane przez kopię. Aby dowiedzieć się, która instrukcja jest poprawna, najpierw musimy przeanalizować, co jest (a co nie) referencją w PHP.
Czym nie są referencje w PHP?
Ważniejsza od wiedzy o tym, jakie referencje są w PHP, jest wiedza o tym, czym one nie są. W PHP referencje nie są wskaźnikami w stylu C; nie można wykonywać operacji arytmetycznych na referencjach, tak jak na wskaźnikach C. Czemu? Ponieważ, w przeciwieństwie do C, referencje PHP nie są tak naprawdę adresami pamięci, ponieważ nie są liczbami wskazującymi lokalizację pamięci. Ale czym są referencje?
Czym są referencje w PHP?
W PHP referencje to „aliasy”, które umożliwiają dwóm różnym zmiennym odczytywanie i zapisywanie pojedynczej wartości. Innymi słowy, są to mechanizmy, które umożliwiają dostęp do tej samej wartości ze zmiennych o różnych nazwach, dzięki czemu zachowują się tak, jakby były tą samą zmienną. Należy pamiętać, że w PHP nazwy zmiennych i zawartość zmiennych to dwie zupełnie różne rzeczy, połączone w tak zwaną „tablicę symboli”. Tak więc, kiedy tworzymy odwołanie, po prostu dodaje alias dla tej zmiennej w tabeli symboli. Załóżmy, że mamy następujący kod:
$a = new Foo();
Po wykonaniu powyższej instrukcji w pamięci tworzona jest zmienna $a
, w pamięci tworzony jest obiekt typu Foo
, a do tablicy symboli dodawany jest wpis, który wskazuje, że zmienna $a
„odwołuje się” (lub jest powiązana z lub wskazuje na obiekt Foo
lub jakkolwiek chcesz go nazwać, ale nie jest wskaźnikiem do tego obiektu per se. Koncepcyjnie mamy coś takiego jak ta ilustracja:
Pop quiz: Co się stanie, jeśli to wykonamy?
$b = $a;
Nie jest tak, że $b
staje się odwołaniem do $a
; nie możemy też powiedzieć, że $b
jest kopią $a
. Tak naprawdę utworzyliśmy nową zmienną $b
w pamięci, a następnie dodaliśmy nowy wpis w tabeli symboli wskazujący, że zmienna $b
również odwołuje się do tego samego obiektu Foo
, co $a
. Tak więc wizualnie mamy coś podobnego do tego, co pokazano na tej ilustracji:
Teraz, jeśli wykonamy:
$c = &$a;
Utworzymy trzecią zmienną $c
w pamięci, ale nie nowy wpis w tablicy symboli dla $c
. Zamiast tego w tabeli symboli zapisano, że $c
jest aliasem $a
, więc będzie zachowywać się identycznie, ale $c
nie jest wskaźnikiem do $a
w przeciwieństwie do C, które tworzy coś, co nazywa się wskaźnikami do wskaźników . Aby zwizualizować, mamy coś podobnego do tego, co pokazano na tej ilustracji:

Gdy tylko będziemy chcieli zmodyfikować wartość którejkolwiek z tych trzech zmiennych (tj. napisać nową wartość), PHP będzie musiało utworzyć w pamięci nową strukturę z_val
, aby oddzielić zawartość zmiennej $b
od pary $a
/ $c
, aby każdy z nich mógł być modyfikowany niezależnie bez wpływu na wartość drugiej. Tak więc, jeśli dodamy następujący wiersz do poprzedniego skryptu:
$b = new Bar();
W pamięci będziemy mieli sytuację przedstawioną na poniższej ilustracji:
Rozważmy teraz pełniejszy przykład:
<?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);
Wynikiem implementacji powyższego skryptu jest:
$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
Zbieranie śmieci PHP
Na koniec zobaczmy, jak działa odśmiecanie PHP, ponieważ zostało wprowadzone w wersji 5.3. Obiekt lub zmienna w pamięci PHP zostanie usunięta przez moduł odśmiecania PHP, gdy w tabeli symboli nie ma odniesień do tego obiektu. Oznacza to, że PHP utrzymuje licznik odwołań obiektu od momentu jego utworzenia, dzięki czemu podczas wykonywania skryptu PHP licznik zwiększa i zmniejsza licznik odwołań w oparciu o zmienne, które „wskazują” na niego. Gdy licznik referencji osiągnie 0 (tj. nic nie odwołuje się do tego obiektu i dlatego nie jest używane), PHP oznaczy ten obiekt jako usuwalny, aby w następnym przebiegu odśmiecacza PHP został usunięty z pamięci , zwalniając tę przestrzeń do ponownego wykorzystania. Jeśli chcesz uzyskać więcej szczegółowych informacji o tym, jak działa odśmiecanie PHP, przeczytaj tę dokumentację.
Myśli zamykające
Mam nadzieję, że wyjaśniłem trochę, jak PHP obsługuje obiekty i zmienne w pamięci i jak „wybiera” obiekty, które powinny zostać usunięte przez garbage collector PHP.
Teraz, gdy już wiesz, jak PHP zarządza wewnętrznie zmiennymi i obiektami w pamięci, chwyć laptopa i zacznij eksperymentować z kodem, aby udowodnić, czego się nauczyłeś. Spróbuj pobawić się zmiennymi i referencjami. Poeksperymentuj również z tym, jak zmiana wartości zmiennej może wpłynąć na wartość innej, do której się odwołuje. Oto pytanie do Ciebie: Jakie będą wartości $a
i $b
po wykonaniu poniższego kodu?
$a = '1'; $b = &$a; $b = "2$b";
Jeśli chcesz przeczytać więcej o funkcjach wydajności PHP, zapoznaj się z tym postem autorstwa innego Toptalera Vilsona Duki.