Introducere în obiecte și referințe în memoria PHP

Publicat: 2022-03-11

Prima dată am redactat acest articol în timp ce studiam pentru certificarea mea PHP, într-un efort de a înțelege mai bine modul în care PHP gestionează variabilele și obiectele din memorie. După multe cercetări, mi-am dat seama că nu este ușor să găsesc răspunsuri la întrebările mele, așa că odată ce am terminat, am decis să documentez informațiile pentru ca oamenii să le găsească pe toate într-un singur loc.

În acest articol, voi vorbi despre modul în care referințele la obiecte și variabile sunt controlate în memorie, deoarece aceasta este o problemă care poate genera discuții și opinii diferite. O întrebare la care trebuie să ne gândim este: „În mod implicit, obiectele sunt transmise prin referință sau prin copiere în PHP?” Voi vorbi mai întâi despre ce referințe nu sunt în PHP; în al doilea rând, voi discuta despre ce sunt acestea și, în final, voi examina cum funcționează colectorul de gunoi în PHP.

Cum creează PHP obiecte în memorie atunci când execută o declarație de genul $a = new Foo(); ? În zilele noastre, memoria nu este o resursă atât de costisitoare și limitată precum era în trecut. Cu toate acestea, este încă important pentru dezvoltatorii PHP buni să cunoască și să înțeleagă cum variabilele și obiectele sunt gestionate intern în timpul execuției aplicației lor.

Obiecte și referințe în memoria PHP și colectarea de gunoi PHP

Obiecte și referințe în PHP

Mulți oameni spun, în cărțile PHP și online, că obiectele în PHP sunt transmise implicit în mod implicit. Alții spun că obiectele în PHP sunt alocate prin copie. Pentru a ne da seama care afirmație este corectă, mai întâi trebuie să analizăm ce este (și ce nu este) o referință în PHP.

Ce nu sunt referințe în PHP?

Mai important decât să știi ce referințe sunt în PHP este să știi ce nu sunt. În PHP, referințele nu sunt pointeri în stil C; nu puteți face operații aritmetice cu referințe așa cum puteți face cu pointerii C. De ce? Pentru că, spre deosebire de C, referințele PHP nu sunt cu adevărat adrese de memorie, deoarece nu sunt numere care indică o locație de memorie. Dar atunci, ce sunt referințele?

Ce sunt referințele în PHP?

În PHP, referințele sunt „alias-uri” care permit două variabile diferite să citească și să scrie o singură valoare. Cu alte cuvinte, sunt mecanisme care permit accesul la aceeași valoare din variabile cu nume diferite, astfel încât acestea să se comporte ca și cum ar fi aceeași variabilă. Rețineți că în PHP, numele variabilelor și conținutul variabilelor sunt două lucruri complet diferite, legate în ceea ce se numește „tabelul de simboluri”. Deci, atunci când creăm o referință, pur și simplu adaugă un alias pentru acea variabilă în tabelul cu simboluri. Să presupunem că avem următorul cod:

 $a = new Foo();

Când se execută instrucțiunea de mai sus, variabila $a este creată în memorie, un obiect de tip Foo este creat în memorie și se adaugă o intrare la tabelul de simboluri care indică faptul că variabila $a „face referire” (sau este legată de , sau indică, sau cum doriți să-l numiți) obiectul Foo , dar nu este un pointer către acel obiect, în sine. Din punct de vedere conceptual, avem ceva de genul acestei ilustrații:

Referințe la obiecte în memoria PHP

Test pop: Ce se întâmplă dacă executăm acest lucru?

 $b = $a;

Nu este că $b devine o referință pentru $a ; nici nu putem spune că $b este o copie a lui $a . Ceea ce s-a întâmplat cu adevărat este că am creat o nouă variabilă $b în memorie și apoi am adăugat o nouă intrare în tabelul de simboluri care indică faptul că variabila $b face referire și la același obiect Foo pe care îl face $a . Deci, vizual, avem ceva similar cu ceea ce este arătat în această ilustrație:

Referințe la obiecte în memoria PHP

Acum, dacă executăm:

 $c = &$a;

Vom fi creat o a treia variabilă $c în memorie, dar nu o nouă intrare în tabelul de simboluri pentru $c . În schimb, în ​​tabelul de simboluri, se înregistrează că $c este un alias pentru $a , deci se va comporta identic, dar $c nu este un pointer către $a — spre deosebire de C, care creează ceva numit pointeri către pointeri . Pentru a vizualiza, avem ceva similar cu ceea ce este arătat în această ilustrație:

Diagrama variabilelor și aliasurile acestora

De îndată ce dorim să modificăm valoarea oricăreia dintre aceste trei variabile (adică să scriem o nouă valoare), PHP va trebui să creeze o nouă structură z_val în memorie pentru a separa conținutul variabilei $b și perechea $a / $c astfel încât să poată fi modificate fiecare în mod independent fără a afecta valoarea celuilalt. Deci, dacă adăugăm următoarea linie la scriptul anterior:

 $b = new Bar();

În memorie, vom avea o situație așa cum este reprezentată în următoarea ilustrație:

Reprezentare grafică a situației descrise mai sus

Acum, să luăm în considerare un exemplu mai complet:

 <?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);

Rezultatul produs de implementarea scriptului de mai sus este:

 $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 Garbage Collection

În sfârșit, să vedem cum funcționează PHP garbage collection, deoarece a fost introdus în versiunea 5.3. Un obiect sau o variabilă din memoria PHP va fi eliminată de colectorul de gunoi PHP atunci când nu există referințe la acel obiect în tabelul de simboluri. Adică, PHP menține un contor de referințe al unui obiect din momentul în care este creat, astfel încât în ​​timpul execuției script-ului PHP, contorul crește și decrește acel contor de referință pe baza variabilelor care „indică” către el. Odată ce numărul de referințe ajunge la 0 (adică, nimic nu face referire la acel obiect și, prin urmare, nu este utilizat), PHP marchează acel obiect ca detașabil, astfel încât în ​​următoarea trecere a colectorului de gunoi PHP, acesta va fi eliminat din memorie. , eliberând acel spațiu pentru reutilizare. Dacă doriți mai multe detalii aprofundate despre cum funcționează PHP garbage collection, citiți această documentație.

Gânduri de închidere

Sper că am clarificat puțin modul în care PHP gestionează obiectele și variabilele din memorie și cum „selectează” obiectele care ar trebui să fie eliminate de colectorul de gunoi PHP.

Acum că înțelegeți modul în care PHP gestionează variabilele și obiectele din memorie în interior, luați laptopul și începeți să experimentați cu ceva cod pentru a demonstra ceea ce ați învățat. Încercați să jucați cu variabile și referințe. De asemenea, experimentați cum modificarea valorii unei variabile ar putea afecta valoarea altei care o face referire. Iată o întrebare pentru tine: Care vor fi valorile lui $a și $b după ce codul de mai jos este executat?

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

Dacă sunteți interesat să citiți mai multe despre caracteristicile de performanță PHP, consultați această postare a lui Toptaler Vilson Duka.