Introducción a Objetos y Referencias en Memoria PHP
Publicado: 2022-03-11Redacté este artículo por primera vez mientras estudiaba para obtener mi certificación de PHP en un esfuerzo por comprender mejor cómo PHP administra las variables y los objetos en la memoria. Después de mucha investigación, me di cuenta de que no era fácil encontrar respuestas a mis preguntas, así que una vez que terminé, decidí documentar la información para que las personas puedan encontrarla en un solo lugar.
En este artículo hablaré sobre cómo se controlan las referencias a objetos y variables en la memoria, ya que este es un tema que puede generar discusión y opiniones divergentes. Una pregunta para reflexionar es: "Por defecto, ¿los objetos se pasan por referencia o por copia en PHP?" Voy a hablar primero sobre qué referencias no hay en PHP; en segundo lugar, discutiré qué son y, finalmente, examinaré cómo funciona el recolector de basura en PHP.
¿Cómo PHP crea objetos en la memoria cuando realiza una declaración como $a = new Foo();
? Hoy en día, la memoria no es un recurso tan caro y limitado como lo era en el pasado. Sin embargo, sigue siendo importante que los buenos desarrolladores de PHP conozcan y comprendan cómo se gestionan internamente las variables y los objetos durante la ejecución de su aplicación.
Objetos y Referencias en PHP
Mucha gente dice, en libros de PHP y en línea, que los objetos en PHP se pasan por referencia de manera predeterminada. Otros dicen que los objetos en PHP se asignan por copia. Para averiguar qué declaración es correcta, primero tenemos que analizar qué es (y qué no es) una referencia en PHP.
¿Qué no son referencias en PHP?
Más importante que saber qué referencias hay en PHP es saber qué no son. En PHP, las referencias no son punteros de estilo C; no puede hacer operaciones aritméticas con referencias como puede hacerlo con punteros C. ¿Por qué? Porque, a diferencia de C, las referencias de PHP no son realmente direcciones de memoria, ya que no son números que indican una ubicación de memoria. Pero entonces, ¿qué son las referencias?
¿Qué son las referencias en PHP?
En PHP, las referencias son "alias" que permiten que dos variables diferentes lean y escriban un solo valor. Dicho de otro modo, son mecanismos que permiten acceder a un mismo valor desde variables con diferente nombre para que se comporten como si fueran la misma variable. Tenga en cuenta que en PHP, los nombres de las variables y el contenido de las variables son dos cosas completamente diferentes, vinculadas en lo que se llama la "tabla de símbolos". Entonces, cuando creamos una referencia, simplemente agrega un alias para esa variable en la tabla de símbolos. Supongamos que tenemos el siguiente código:
$a = new Foo();
Cuando se ejecuta la instrucción anterior, la variable $a
se crea en la memoria, un objeto de tipo Foo
se crea en la memoria y se agrega una entrada a la tabla de símbolos que indica que la variable $a
"hace referencia" (o está relacionada con , o apunta a, o como quiera llamarlo) el objeto Foo
, pero no es un puntero a ese objeto, per se. Conceptualmente, tenemos algo como esta ilustración:
Prueba sorpresa: ¿Qué sucede si ejecutamos esto?
$b = $a;
No es que $b
se convierta en una referencia de $a
; tampoco podemos decir que $b
es una copia de $a
. Lo que realmente sucedió es que creamos una nueva variable $b
en la memoria y luego agregamos una nueva entrada en la tabla de símbolos que indica que la variable $b
también hace referencia al mismo objeto Foo
que hace $a
. Entonces, visualmente, tenemos algo similar a lo que se muestra en esta ilustración:
Ahora bien, si ejecutamos:
$c = &$a;
Habremos creado una tercera variable $c
en memoria, pero no una nueva entrada en la tabla de símbolos para $c
. En cambio, en la tabla de símbolos, se registra que $c
es un alias para $a
, por lo que se comportará de manera idéntica, pero $c
no es un puntero a $a
diferencia de C, que crea algo llamado punteros a punteros . Para visualizar, tenemos algo similar a lo que se muestra en esta ilustración:

En cuanto queramos modificar el valor de alguna de estas tres variables (es decir, escribir un nuevo valor), PHP tendrá que crear una nueva estructura z_val
en memoria para separar el contenido de la variable $b
y el par $a
/ $c
para que cada uno pueda modificarse independientemente sin afectar el valor del otro. Entonces, si agregamos la siguiente línea al script anterior:
$b = new Bar();
En memoria, tendremos una situación como la representada en la siguiente ilustración:
Ahora, consideremos un ejemplo más 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);
El resultado producido por la implementación del script anterior es:
$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
Recolección de basura de PHP
Finalmente, veamos cómo funciona la recolección de basura de PHP, ya que se introdujo en la versión 5.3. El recolector de basura de PHP eliminará un objeto o variable en la memoria de PHP cuando no haya referencias a ese objeto en la tabla de símbolos. Es decir, PHP mantiene un contador de referencias de un objeto desde el momento en que se crea para que, durante la ejecución del script PHP, el contador incremente y disminuya ese contador de referencia en función de las variables que lo “apuntan”. Una vez que el recuento de referencias llega a 0 (es decir, nada hace referencia a ese objeto y, por lo tanto, no se usa), PHP marca ese objeto como extraíble, de modo que en la siguiente pasada del recolector de elementos no utilizados de PHP, se eliminará de la memoria. , liberando ese espacio para su reutilización. Si desea obtener más detalles sobre cómo funciona la recolección de elementos no utilizados de PHP, lea esta documentación.
Pensamientos finales
Espero haber aclarado un poco cómo maneja PHP los objetos y las variables en la memoria y cómo “selecciona” los objetos que el recolector de basura de PHP debe eliminar.
Ahora que comprende cómo PHP administra internamente las variables y los objetos en la memoria, tome su computadora portátil y comience a experimentar con algún código para demostrar lo que ha aprendido. Intenta jugar con variables y referencias. Además, experimente cómo cambiar el valor de una variable podría afectar el valor de otra referencia. Aquí hay una pregunta para usted: ¿Cuáles serán los valores de $a
y $b
después de que se ejecute el siguiente código?
$a = '1'; $b = &$a; $b = "2$b";
Si está interesado en leer más sobre las funciones de rendimiento de PHP, consulte esta publicación del compañero Toptaler Vilson Duka.