Wprowadzenie do obiektów i referencji w pamięci PHP

Opublikowany: 2022-03-11

Po 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 , 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 pamięci PHP i odśmiecaniu PHP

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 referencje?

Czym 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:

Referencje do obiektów w pamięci PHP

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:

Referencje do obiektów w pamięci PHP

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:

Schemat zmiennych i ich aliasy

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:

Graficzne przedstawienie sytuacji opisanej powyżej

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.