Lo que es mejor para liberar memoria con PHP: unset () o $ var = null

244

Me doy cuenta de que el segundo evita la sobrecarga de una llamada de función ( actualización , en realidad es una construcción de lenguaje), pero sería interesante saber si uno es mejor que el otro. He estado usando la unset()mayor parte de mi codificación, pero recientemente he revisado algunas clases respetables que se encuentran en la red y que en su $var = nulllugar usan .

¿Hay uno preferido y cuál es el razonamiento?

alex
fuente

Respuestas:

234

Se mencionó en la página del manual sin configurar en 2009 :

unset()hace exactamente lo que dice su nombre: desarmar una variable. No fuerza la liberación inmediata de memoria. El recolector de basura de PHP lo hará cuando lo crea conveniente, por intención tan pronto como esos ciclos de CPU no sean necesarios de todos modos, o tan tarde como antes de que el script se quede sin memoria, lo que ocurra primero.

Si lo está haciendo $whatever = null;, está reescribiendo los datos de la variable. Es posible que la memoria se libere / reduzca más rápido, pero puede robar ciclos de CPU del código que realmente los necesita antes, lo que resulta en un tiempo de ejecución general más largo.

(Desde 2013, esa unsetpágina de manual ya no incluye esa sección)

Tenga en cuenta que hasta php5.3, si tiene dos objetos en referencia circular , como en una relación padre-hijo, llamar a unset () en el objeto padre no liberará la memoria utilizada para la referencia padre en el objeto hijo. (Tampoco se liberará la memoria cuando se recolecte basura el objeto principal.) ( Error 33595 )


La pregunta " diferencia entre unset y = nulo " detalla algunas diferencias:


unset($a)también elimina $ade la tabla de símbolos; por ejemplo:

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

Salidas:

Notice: Undefined variable: a in xxx
NULL

Pero cuando $a = nullse usa:

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:

NULL

Parece que $a = nulles un poco más rápido que su unset()contraparte: actualizar una entrada de la tabla de símbolos parece ser más rápido que eliminarla.


  • cuando intente utilizar una unsetvariable no existente ( ), se activará un error y el valor de la expresión de la variable será nulo. (Porque, ¿qué más debe hacer PHP? Cada expresión debe tener algún valor).
  • Sin embargo, una variable con nulo asignado sigue siendo una variable perfectamente normal.
VonC
fuente
18
Tenga en cuenta que si $whateverapunta a un objeto, $whatever = nullsobrescribe el puntero, no el objeto en sí mismo, por lo que actúa básicamente igual que unset().
Gras Double
1
@VonC: la cita no establecida en php.net a la que te refieres ya no existe.
Jürgen Thelen
@ JürgenThelen cierto, pero el contenido de esa vieja respuesta todavía parece relevante, ¿no?
VonC
1
@VonC: Definitivamente. No estoy seguro acerca de "no se necesitan ciclos de CPU" y "antes ... sin memoria" desencadena la recolección de basura. Ver stackoverflow.com/q/20230626/693207 . ¿Quizás puedas arrojar algo de luz?
Jürgen Thelen
1
@Omar He editado la respuesta: la página de manual no configurada de 2009 (he vinculado a la versión de 2009) incluye una sección que ya no está presente en la versión actual de esa misma página.
VonC
48

unseten realidad no es una función, sino una construcción del lenguaje . No es más una llamada de función que returnao an include.

Además de los problemas de rendimiento, el uso unsethace que la intención de su código sea mucho más clara.

Alex Barrett
fuente
Es por eso que siempre los usé, personalmente pensé que se veían mejor que $ var = null. Por cierto, siempre he usado mayúsculas NULL ... pero ahora no sé por qué.
alex
1
@VonC: Sí, pensé eso, pero ¿por qué puedes usar minúsculas verdadero, falso y nulo?
alex
3
@alex, puedes hacer eso sin configurar. Por ejemplo "$ test = 4; (unset) $ test;" - extraño pero cierto, y devuelve el valor de $ test antes de desarmarlo. En cualquier caso, el manual de PHP confirma que es una construcción de lenguaje.
thomasrutter
55
@alex: PSR-2 requiere minúsculas para todas las palabras clave.
Tgr
2
@alex: las palabras clave de PHP no distinguen entre mayúsculas y minúsculas; También podría deletrear unsetcomo UnSeT, por ejemplo. La comunidad se ha decidido por todo en minúsculas como cuestión de estilo, pero otras cubiertas siguen funcionando.
Mark Reed
35

Al hacer un unset () en una variable, esencialmente ha marcado la variable para 'recolección de basura' (PHP realmente no tiene uno, pero por el bien) por lo que la memoria no está disponible de inmediato. La variable ya no aloja los datos, pero la pila permanece en el tamaño más grande. Hacer el método nulo elimina los datos y reduce la memoria de la pila casi de inmediato.

Esto ha sido por experiencia personal y de otros también. Vea los comentarios de la función unset () aquí .

Personalmente uso unset () entre iteraciones en un bucle para no tener que retrasar el tamaño de la pila. Los datos se han ido, pero la huella permanece. En la próxima iteración, php ya está tomando la memoria y, por lo tanto, es más rápido inicializar la siguiente variable.

William Holroyd
fuente
15
Establecer algo en NULL puede ser beneficioso si la memoria requerida para mantener el valor NULL es menor que la requerida para mantener el valor que tenía anteriormente. Por ejemplo, una cadena larga. Si la cadena no era una constante y su recuento de referencia cae a cero, entonces esa memoria debería liberarse. Desarmar es más limpio: ya no mantiene una referencia. Debe esperar la recolección de basura, pero es seguro tratarla como si no ocupara memoria, ya que una condición de poca memoria activará la recolección de basura.
thomasrutter
no podemos usar ambos? igual a nulo y luego desarmado?
Nabeel Khan
2
@NabeelKhan Sugeriría usar unset () dentro de los bucles y luego anularlo cuando salga del bucle. De lo contrario, hay un impacto en el rendimiento al hacer ambas cosas dentro del ciclo. Si no está usando bucles, simplemente anule como ya lo hace la lógica unset () detrás de escena.
William Holroyd
27
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";
?>

Por eso parece que "= nulo" es más rápido.

Resultados de PHP 5.4:

  • tomó 0.88389301300049 segundos
  • tomó 2.1757180690765 segundos

Resultados de PHP 5.3:

  • tomó 1.7235369682312 segundos
  • tomó 2.9490959644318 segundos

Resultados de PHP 5.2:

  • tomó 3.0069220066071 segundos
  • tomó 4.7002630233765 segundos

Resultados de PHP 5.1:

  • tomó 2.6272349357605 segundos
  • tomó 5.0403649806976 segundos

Las cosas comienzan a verse diferentes con PHP 5.0 y 4.4.

5.0:

  • tomó 10.038941144943 segundos
  • tomó 7.0874409675598 segundos

4.4:

  • tomó 7.5352551937103 segundos
  • tomó 6.6245851516724 segundos

Tenga en cuenta que microtime (true) no funciona en PHP 4.4, así que tuve que usar el ejemplo microtime_float que se proporciona en php.net/microtime / Example # 1.


fuente
77
Creo que tu prueba es defectuosa. El primer bucle es una reasignación simple y el segundo bucle destruye y recrea el mismo símbolo. Si la prueba se vuelve a hacer con una matriz, unsetes más rápido. Tengo una prueba que luego verifica la existencia en el unsetcaso. En esa prueba, configurarlo nulles marginalmente más rápido. Prueba: pastebin.com/fUe57C51
Knyri
44
@ansur, siempre llame gc_collect_cyclesantes de iniciar el temporizador para obtener resultados más precisos.
Pacerier
@knyri, ¿puedes enlazar a eso?
Nabeel Khan
@NabeelKhan Ya no tengo los resultados de esa prueba; pero hay un enlace al código de prueba en mi comentario anterior.
Knyri
19

Hace una diferencia con los elementos de la matriz.

Considera este ejemplo

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

Aquí, la clave 'prueba' todavía existe. Sin embargo, en este ejemplo

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

La clave ya no existe.

auris
fuente
18

Funciona de manera diferente para las variables copiadas por referencia:

$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5

$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null
RiaD
fuente
55
Llevo unos años codificando php y nunca he visto "&" acerca de hacer referencia a la var. Gracias + 1 :)
Chris
1
$ a = 78; $ b = $ a; sin establecer ($ a); var_dump ($ b); // 78; var_dump ($ a); // Variable indefinida: a
zloctb
13

Con respecto a los objetos, especialmente en el escenario de carga lenta, uno debe considerar que el recolector de basura se está ejecutando en ciclos inactivos de la CPU, por lo que suponiendo que tenga problemas cuando muchos objetos se cargan, la penalización de poco tiempo resolverá la liberación de memoria.

Use time_nanosleep para permitir que GC recopile memoria. Establecer variable en nulo es deseable.

Probado en el servidor de producción, originalmente el trabajo consumía 50 MB y luego se detuvo. Después de que se usó nanosleep 14MB fue un consumo constante de memoria.

Uno debería decir que esto depende del comportamiento del GC que puede cambiar de una versión de PHP a otra. Pero funciona en PHP 5.3 bien.

p.ej. esta muestra (código tomado del feed de Google VirtueMart2)

for($n=0; $n<count($ids); $n++)
{
    //unset($product); //usefull for arrays
    $product = null
    if( $n % 50 == 0 )
    {
        // let GC do the memory job
        //echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
        time_nanosleep(0, 10000000);
    }

    $product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
    ...
OSP
fuente
3

Todavía dudo sobre esto, pero lo he intentado en mi script y estoy usando xdebug para saber cómo afectará el uso de memoria de mi aplicación. El script se establece en mi función de esta manera:

function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
    $sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
    if($showSql === FALSE) {
        $sql = mysql_query($sql) or die(mysql_error());
        $data = mysql_fetch_array($sql);
        return $data[0];
    } else echo $sql;
}

Y agrego unset antes del returncódigo y me da: 160200, luego trato de cambiarlo $sql = NULLy me da: 160224 :)

Pero hay algo único en esta comparativa cuando no estoy usando unset () o NULL, xdebug me da 160144 como uso de memoria

Entonces, creo que dar línea para usar unset () o NULL agregará el proceso a su aplicación y será mejor mantener el origen con su código y disminuir la variable que está utilizando de la manera más efectiva posible.

Corrígeme si me equivoco, gracias

Anggie Aziz
fuente
Creo que mientras devuelve el elemento $ data [0], se hace referencia a toda la matriz / pero es solo la hipótesis. Intente copiar $ data [0] a la variable local, establezca la matriz en nulo y devuelva la variable local. Aquí hay buenos antecedentes tuxradar.com/practicalphp/18/1/11 y ofc. php.net/manual/en/features.gc.php
OSP
2

Creé una nueva prueba de rendimiento para unsety =null, porque como se menciona en los comentarios, aquí escrito tiene un error (la recreación de los elementos). Usé matrices, como ves, ahora no importaba.

<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = 'a';
    $arr2[$i] = 'a';
}

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = null;
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

Pero solo puedo probarlo en un servidor PHP 5.5.9, aquí los resultados: - tomó 4.4571571350098 segundos - tomó 4.4425978660583 segundos

Prefiero unsetpor razones de legibilidad.

Michael B.
fuente
2

PHP 7 ya está trabajando en estos problemas de administración de memoria y su uso reducido hasta un uso mínimo.

<?php
  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
     $a = 'a';
     unset($a);
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

?>

PHP 7.1 Outpu:

tomó 0.16778993606567 segundos tomó 0.16630101203918 segundos

Swapnil
fuente
1

unsetcódigo si no libera memoria inmediata sigue siendo muy útil y sería una buena práctica hacerlo cada vez que pasemos los pasos del código antes de salir de un método. tenga en cuenta que no se trata de liberar memoria inmediata. la memoria inmediata es para la CPU, ¿qué pasa con la memoria secundaria que es RAM?

y esto también aborda la prevención de pérdidas de memoria.

consulte este enlace http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

He estado usando unset durante mucho tiempo ahora.

mejor práctica como esta en el código para desarmar instantáneamente todas las variables que ya se han utilizado como matriz.

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

y just unset($data);para liberar todo uso variable.

por favor vea el tema relacionado para desarmar

¿Qué tan importante es desarmar variables en PHP?

[insecto]

cero8
fuente
1

Para el registro, y excluyendo el tiempo que lleva:

<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n";

echo "<hr>function:<br>";
function test() {
    $x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

Vuelve

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

Conclusión, memoria libre tanto nula como sin configurar como se esperaba (no solo al final de la ejecución). Además, la reasignación de una variable mantiene el valor dos veces en algún momento (520216 frente a 438352)

magallanes
fuente