1. Variables en PHPVariables en PHP
Cómo funcionan las variables internamente en PHP y el recolector de basura?Cómo funcionan las variables internamente en PHP y el recolector de basura?
2. IntroducciónIntroducción
En PHP las variables se representan con un signo de dólar seguido por el nombre de la variable.
El nombre de la variable es sensible a minúsculas y mayúsculas. El tipo de las variables es
decidido en tiempo de ejecución por PHP dependiendo del contexto en el que se emplea dicha
variable.
3. Estructura de una variable en PHPEstructura de una variable en PHP
Contenedores zval (ZendValue)
value: representa el valor de la variable
refcount: es un integer que indica cuántos símbolos
apuntan a este zval
type: indica el tipo de variable
_isref: es un integer que representa un valor booleano
si ha sido afectado por una referencia
struct _zval_struct {
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
};
El tipo de contenedor que se crea depende del contenido de la variable.
4. Ejemplos:
Esta salida se puede lograr con el comando xdebug_debug_zval
$test = ‘value'; test: (refcount=1, is_ref=0), string ‘value' (length=5)
$test = array(‘key1' => ‘value', 1); test: (refcount=1, is_ref=0), array (size=2)
'key1' => (refcount=1, is_ref=0),string ‘value' (length=5)
0 => (refcount=1, is_ref=0),int 1
class Test{
public $prop = 1;
private $prop1 = ‘val' ;
}
$obj = new Test();
obj: (refcount=1, is_ref=0), object(Test)[1]
public ‘prop' => (refcount=2, is_ref=0),int 1
private ‘prop1' => (refcount=2, is_ref=0), string ‘value'
(length=5)
Solo a los contenidos del arreglo se les crean zval.
Los array y los objetos son tratados como tipos compuestos.
5. Cómo PHP maneja las variablesCómo PHP maneja las variables
Que pasa si le asignamos el contenido de una variable a otra.
$var1 = 10;
$var2 = $var1;
El refcount aumenta en función del número de símbolos que apuntan a un contenedor,
en este caso 2, y es la clave para la administración de la memoria en las variables PHP.
var1: (refcount=2, is_ref=0),int 10
var2: (refcount=2, is_ref=0),int 10
6. Copia en escrituraCopia en escritura
Copy On Write (COW) en ingles es una forma de ahorrar memoria. El sistema (PHP) copia o
mueve a una nueva región de la memoria cuando modificas un símbolo que estaba apuntando
a un zval.
Veamos una serie de ejemplos.
7. $x = "vals";
$x1 = $x;
x: (refcount=2, is_ref=0),string ‘vals' (length=4)
x1: (refcount=2, is_ref=0),string 'vals' (length=4)
$x = 12; x: (refcount=1, is_ref=0),int 12
x1: (refcount=1, is_ref=0),string 'vals ' (length=4)
Si se vuelve a asignar el mismo valor a $x se crea un nuevo zval.
Los zval solo se reutilizan cuando se asigna una variable a otra.
$x = "vals"; x: (refcount=1, is_ref=0),string ‘vals' (length=4)
x1: (refcount=1, is_ref=0),string 'vals ' (length=4)
9. ReferenciasReferencias
Cuando adicionamos una variable por referencia & ocurre lo mismo (dos símbolos apuntando
al mismo zval) pero afecta la forma en que la copia en escritura se comporta.
10. $x = "s"; x: (refcount=1, is_ref=0),string 's' (length=1)
$x1 = &$x; x: (refcount=2, is_ref=1),string 's' (length=1)
x1: (refcount=2, is_ref=1),string 's' (length=1)
Se que cuando is_ref aumenta se ligan los contenedores y el valor se cambio en todos.
$x1 = "a"; x: (refcount=2, is_ref=1),string 'a' (length=1)
x1: (refcount=2, is_ref=1),string 'a' (length=1)
11. $x = "s"; x: (refcount=1, is_ref=0),string 's' (length=1)
$x1 = &$x; x: (refcount=2, is_ref=1),string 's' (length=1)
x1: (refcount=2, is_ref=1),string 's' (length=1)
Otro ejemplo es cuando se asigna una variable con referencia a otra que no, con el
mismo ejemplo anterior.
$x1 = "a"; x: (refcount=2, is_ref=1),string 'a' (length=1)
x1: (refcount=2, is_ref=1),string 'a' (length=1)
$x2 = $x1; x: (refcount=2, is_ref=1),string 'a' (length=1)
x1: (refcount=2, is_ref=1),string 'a' (length=1)
x2: (refcount=1, is_ref=0),string 'a' (length=1)
PHP tiene que asignarla en un nuevo bloque de memoria, y duplicar el string 'hola', debido a la
referencia anterior. Sin la referencia, sólo habría un contenedor zval, y el refcount sería de 3.
Por esto en ocasiones las referencias no ahorran memoria, es al contrario.
12. Recolector de BasuraRecolector de Basura
Es un mecanismo que rastrea el uso de objetos y libera la memoria si no se encuentran en
el ámbito actual o todavía no están siendo usados por el programador.
PHP ya empleaba este concepto desde el principio, pero hasta PHP 5.3 no liberaba la memoria
de forma automática. A partir de PHP 5.3 surgió Zend Garbage Collector, o Zend GC. Lo más
importante de esta nueva característica es el tratamiento de las referencias circulares.
Las referencias circulares aparecen con tipos compuestos, como arrays y objetos. Cuando dos tipos
compuestos contienen referencias el uno al otro o cuando un tipo compuesto tiene una referencia
a si mismo, se produce una referencia circular. Así la cosa se complica a la hora de liberar memoria,
y antes de PHP 5.3 no se liberaba correctamente.
Ahora PHP rastrea automáticamente referencias circulares y las libera de vez en cuando.
Si sabes que tu código tiene referencias circulares y quieres forzar a PHP a que las libere,
puedes utilizar la función gc_collect_cycles().
13. Ejemplo de referencia cíclica:
$x = array(‘a' => ‘val', ‘b' => 1);
$x['c'] = $x['a'];
xdebug_debug_zval('x');
********************************
x: (refcount=1, is_ref=0), array(
'a' => (refcount=2, is_ref=0), string 'val'
'b' => (refcount=1, is_ref=0), int 1
'c' => (refcount=2, is_ref=0), string 'val'
)
Aunque xdebug muestre dos contenedores zval de valor val, se refiere al mismo