Introduzione a oggetti e riferimenti nella memoria PHP
Pubblicato: 2022-03-11Ho scritto questo articolo per la prima volta mentre studiavo per la mia certificazione PHP nel tentativo di capire meglio come PHP gestisce le variabili e gli oggetti in memoria. Dopo molte ricerche, mi sono reso conto che non era facile trovare le risposte alle mie domande, quindi una volta terminato, ho deciso di documentare le informazioni in modo che le persone possano trovarle tutte in un unico posto.
In questo articolo parlerò di come vengono controllati in memoria i riferimenti a oggetti e variabili, poiché questo è un problema che può generare discussioni e opinioni divergenti. Una domanda su cui riflettere è: "Per impostazione predefinita, gli oggetti vengono passati per riferimento o per copia in PHP?" Parlerò prima di quali riferimenti non sono in PHP; in secondo luogo, discuterò di cosa sono e, infine, esaminerò come funziona il Garbage Collector in PHP.
In che modo PHP crea oggetti in memoria quando esegue un'istruzione come $a = new Foo();
? Al giorno d'oggi, la memoria non è una risorsa così costosa e limitata come lo era in passato. Tuttavia, è ancora importante che i bravi sviluppatori PHP conoscano e comprendano come le variabili e gli oggetti vengono gestiti internamente durante l'esecuzione della loro applicazione.
Oggetti e riferimenti in PHP
Molte persone affermano, nei libri PHP e online, che gli oggetti in PHP vengono passati per riferimento per impostazione predefinita. Altri dicono che gli oggetti in PHP sono allocati per copia. Per capire quale affermazione è corretta, dobbiamo prima analizzare cosa è (e cosa non lo è) un riferimento in PHP.
Cosa non sono i riferimenti in PHP?
Più importante che sapere quali riferimenti ci sono in PHP è sapere cosa non sono. In PHP, i riferimenti non sono puntatori in stile C; non puoi eseguire operazioni aritmetiche con riferimenti come puoi con i puntatori C. Come mai? Perché, a differenza di C, i riferimenti PHP non sono realmente indirizzi di memoria, poiché non sono numeri che indicano una posizione di memoria. Ma allora, cosa sono i riferimenti?
Cosa sono i riferimenti in PHP?
In PHP, i riferimenti sono "alias" che consentono a due diverse variabili di leggere e scrivere un singolo valore. In altre parole, sono meccanismi che consentono l'accesso allo stesso valore da variabili con nomi diversi in modo che si comportino come se fossero la stessa variabile. Tieni presente che in PHP, i nomi delle variabili e il contenuto delle variabili sono due cose completamente diverse, collegate in quella che viene chiamata la "tabella dei simboli". Quindi, quando creiamo un riferimento, aggiunge semplicemente un alias per quella variabile nella tabella dei simboli. Supponiamo di avere il seguente codice:
$a = new Foo();
Quando viene eseguita l'istruzione precedente, viene creata in memoria la variabile $a
, in memoria viene creato un oggetto di tipo Foo
e viene aggiunta una voce alla tabella dei simboli che indica che la variabile $a
"riferisce" (o è correlata a , o punta a, o come vuoi chiamarlo) l'oggetto Foo
, ma non è un puntatore a quell'oggetto, di per sé. Concettualmente, abbiamo qualcosa come questa illustrazione:
Pop quiz: cosa succede se eseguiamo questo?
$b = $a;
Non è che $b
diventi un riferimento di $a
; né possiamo dire che $b
è una copia di $a
. Ciò che è realmente accaduto è che abbiamo creato una nuova variabile $b
in memoria e quindi aggiunto una nuova voce nella tabella dei simboli indicando che la variabile $b
fa riferimento anche allo stesso oggetto Foo
che fa $a
. Quindi, visivamente, abbiamo qualcosa di simile a ciò che viene mostrato in questa illustrazione:
Ora, se eseguiamo:
$c = &$a;
Avremo creato una terza variabile $c
in memoria, ma non una nuova voce nella tabella dei simboli per $c
. Invece, nella tabella dei simboli, viene registrato che $c
è un alias per $a
, quindi si comporterà in modo identico, ma $c
non è un puntatore a $a
, a differenza di C, che crea qualcosa chiamato pointers to pointers . Per visualizzare, abbiamo qualcosa di simile a quello mostrato in questa illustrazione:

Non appena vogliamo modificare il valore di una qualsiasi di queste tre variabili (cioè, scrivere un nuovo valore), PHP dovrà creare una nuova struttura z_val
in memoria per separare il contenuto della variabile $b
e la coppia $a
/ $c
in modo che ciascuno possa essere modificato indipendentemente senza influire sul valore dell'altro. Quindi, se aggiungiamo la seguente riga allo script precedente:
$b = new Bar();
In memoria, avremo una situazione come rappresentata nella seguente illustrazione:
Consideriamo ora un esempio più completo:
<?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);
L'output prodotto dall'implementazione dello script di cui sopra è:
$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
Raccolta di rifiuti PHP
Infine, vediamo come funziona la Garbage Collection di PHP, dal momento che è stata introdotta nella versione 5.3. Un oggetto o una variabile nella memoria PHP verranno rimossi dal Garbage Collector PHP quando non ci sono riferimenti a quell'oggetto nella tabella dei simboli. Cioè, PHP mantiene un contatore di riferimenti di un oggetto dal momento in cui viene creato in modo che durante l'esecuzione dello script PHP, il contatore incrementi e decrementi quel contatore di riferimento in base alle variabili che "puntano" ad esso. Una volta che il conteggio dei riferimenti raggiunge 0 (cioè, nulla fa riferimento a quell'oggetto e, quindi, non viene utilizzato), PHP contrassegna quell'oggetto come rimovibile, in modo che nel passaggio successivo del Garbage Collector PHP, verrà rimosso dalla memoria , liberando quello spazio per il riutilizzo. Se desideri dettagli più approfonditi su come funziona la garbage collection di PHP, leggi questa documentazione.
Pensieri di chiusura
Spero di aver chiarito un po' come PHP gestisce oggetti e variabili in memoria e come "seleziona" gli oggetti che dovrebbero essere rimossi dal Garbage Collector di PHP.
Ora che hai capito come PHP gestisce internamente le variabili e gli oggetti in memoria, prendi il tuo laptop e inizia a sperimentare del codice per dimostrare ciò che hai imparato. Prova a giocare con variabili e riferimenti. Inoltre, sperimenta come la modifica del valore di una variabile potrebbe influenzare il valore di un'altra che fa riferimento ad essa. Ecco una domanda per te: quali saranno i valori di $a
e $b
dopo l'esecuzione del codice seguente?
$a = '1'; $b = &$a; $b = "2$b";
Se sei interessato a leggere di più sulle funzionalità di prestazioni di PHP, dai un'occhiata a questo post del collega Toptaler Vilson Duka.