Posts etiquetados ‘garbage collector’


You can read the English version of this post in http://phpsblog.agustinvillalba.com/objects-and-references-in-memory-in-php/

Hoy vamos a hablar sobre cómo PHP crea los objetos en memoria cuando realizamos una instrucción del tipo $a = new Foo(); y sobre cómo son gestionadas las referencias en memoria, dado que este es un tema que puede generar debate y diferencia de opiniones. Para ello hablaremos de lo qué NO son las referencias en PHP. Por último, veremos cómo funciona el recolector de basura (o garbage collector) en PHP.

Objetos y referencias en PHP

Una afirmación que está muy extendida en los libros de PHP, y en la red en general, es que en PHP los objetos, por defecto, se pasan por referencia. También hay quienes dicen que los objetos en PHP se asignan por copia. Estas afirmaciones no son completamente ciertas, y para comprobarlo primero hemos de analizar qué son (y qué NO son) las referencias en PHP.

Qué NO son las referencias en PHP

Creo que más importante que saber qué son las referencias en PHP, hemos de aclarar qué NO son las referencias en PHP. En PHP las referencias no son punteros al estilo C, no podemos realizar operaciones aritméticas con las referencias como se puede hacer en C. ¿Por qué? Porque en PHP las referencias no son en realidad direcciones de memoria como en C, no son números que indican una posición de memoria. Pero entonces… ¿qué son en realidad las referencias?

Qué son las referencias en PHP

En PHP, las referencias son «alias» que permiten que 2 variables distintas puedan escribir sobre un mismo valor. O visto de otra manera, son un mecanismo que nos permiten acceder un mismo valor desde nombre de variables distintos y que se comporten como si fueran la misma variable. Hay que tener en cuenta que, en PHP, el nombre de la variable y el contenido de esa variable son 2 cosas totalmente distintas que se enlazan en lo que se llama la tabla de símbolos. De forma que cuando creamos una referencia, simplemente se está añadiendo un alias de dicha variable en la tabla de símbolos de PHP. Veámoslo con un ejemplo, supongamos que tenemos el siguiente código:

$a = new Foo();

Cuando se ejecuta la instrucción anterior, realmente lo que está sucediendo es que se crea una variable «a» en memoria, se crea un objeto de tipo Foo en memoria y se añade una entrada en la tabla de símbolos de PHP en la que se indica que la variable $a «referencia» (o se relaciona, o «apunta», o como se quiera llamar) al objeto Foo, pero NO es un puntero a dicho objeto. Si a continuación ejecutamos la instrucción:

$b = $a;

Lo que ocurre no es que se $b se convierta en una referencia de $a, tampoco se puede decir que $b sea una copia de $a. Lo que realmente ha sucedido es que se ha creado una nueva variable «b» en memoria y luego se ha añadido una nueva entrada en la tabla de símbolos indicando que la variable $b, también «referencia» al mismo objeto Foo que $a. Si a continuación ejecutamos la instrucción:

$c = &$a;

Lo que ocurre es que en memoria se habrá creado una tercera variable «c» pero NO se añade una nueva entrada en la tabla de símbolos para «c», sino que en la tabla de símbolos se indica que «c» es un alias de «a», por lo tanto se comportará de forma idéntica que esta, pero no es que «c» sea un puntero a «a» (lo que en C se conoce como punteros a punteros).
Veamos un ejemplos más completo:

<?php

class myClass {
    public $var;
		
    function __construct() {
	$this->var = 1;
    }

    function inc() { return ++$this->var; }
}

$a = new myClass(); // $a "referencia" a un objeto Foo
$b = $a; //b también referencia al mismo objeto Foo que a
//($a) == ($b) == <id> del objeto Foo, pero a y b son entradas distintas en la tabla de símbolos

echo "\$a = ";var_dump($a);
echo "\$b = ";var_dump($b);

$c = &$a; //$c es un alias de $a
//($a, $c) == <id> del objeto Foo, c es un alias de a en la tabla de símbolos
echo "\$c = ";var_dump($c);

$a = NULL;
//Se elimina la entrada en la tabla de símbolos donde se relacionaba a "$a" con el objeto Foo
//Al eliminarse esta entrada, $c también deja de estar relacionado con Foo
//A todos los efectos, Foo sigue existiendo en memoria, y sigue estando relacionado con $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;
//Se elimina la entrada en la tabla de símbolos donde se relacionaba a "$b" con el objeto Foo
//Ya no hay más entradas en la tabla de símbolos que se relacionen con Foo,
//Por lo que Foo ha dejado de estar referenciado y puede ser eliminado por el garbage collector

echo "\$b = ";var_dump($b);

Las salidas que produce la ejecución del script anterior son:

$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

Garbage collector en PHP

Por último, veamos cómo funciona el garbage collector (o recolector de basura) en PHP. Un objeto o una variable en PHP podrá ser eliminada de memoria por el garbage collector cuando no exista ninguna referencia a dicho objeto en la tabla de símbolos. Es decir, PHP mantiene un contador de referencias a un objeto desde el momento en que éste es creado, de forma que durante la ejecución del script PHP va incrementando y decrementando dicho contador de referencias en función de las variables que le van «apuntando». Una vez ese contador de referencias llega a 0 (es decir, nadie está relacionado con ese objeto y por lo tanto, no se está utilizando dicho objeto), PHP marca ese objeto como basura o eliminable, de forma que en la siguiente pasada del garbage collector, éste eliminará dicho objeto de memoria y esa posición de memoria será utilizable por PHP nuevamente.

De esta forma esperamos haber aclarado un poco más cómo gestiona PHP los objetos y variables en memoria y cómo se «seleccionan» los objetos que han de ser eliminados por el recolector de basura en PHP.

You can read the English version of this post in http://phpsblog.agustinvillalba.com/objects-and-references-in-memory-in-php/