Cosa c'è di meglio nel liberare memoria con PHP: unset () o $ var = null


244

Mi rendo conto che il secondo evita il sovraccarico di una chiamata di funzione (l' aggiornamento , in realtà è un costrutto di linguaggio), ma sarebbe interessante sapere se uno è migliore dell'altro. Ho usato unset()per la maggior parte della mia codifica, ma di recente ho esaminato alcune classi rispettabili trovate fuori dalla rete che usano $var = nullinvece.

Ce n'è uno preferito e qual è il ragionamento?

Risposte:


234

È stato menzionato nella pagina del manuale non impostato nel 2009 :

unset()fa esattamente quello che dice il suo nome: disinserisce una variabile. Non forza la liberazione immediata della memoria. Il garbage collector di PHP lo farà quando lo riterrà opportuno, di proposito non appena questi cicli CPU non saranno comunque necessari o fino a quando lo script si esaurirebbe, qualunque cosa accada per prima.

Se lo stai facendo, $whatever = null;stai riscrivendo i dati della variabile. Potresti liberare / ridurre la memoria più velocemente, ma potrebbe rubare i cicli della CPU dal codice che ne ha veramente bisogno prima, risultando in un tempo di esecuzione complessivo più lungo.

(Dal 2013, quella unsetpagina man non include più quella sezione)

Si noti che fino a php5.3, se si hanno due oggetti con riferimento circolare , ad esempio in una relazione padre-figlio, la chiamata di unset () sull'oggetto padre non libererà la memoria utilizzata per il riferimento padre nell'oggetto figlio. (Né la memoria verrà liberata quando l'oggetto genitore viene raccolto in modo inutile .) ( Bug 33595 )


La domanda " differenza tra unset e = null " descrive alcune differenze:


unset($a)rimuove anche $adalla tabella dei simboli; per esempio:

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

Uscite:

Notice: Undefined variable: a in xxx
NULL

Ma quando $a = nullviene utilizzato:

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

NULL

Sembra che $a = nullsia un po 'più veloce della sua unset()controparte: l'aggiornamento di una voce della tabella dei simboli sembra essere più veloce della sua rimozione.


  • quando si tenta di utilizzare una unsetvariabile inesistente ( ), verrà generato un errore e il valore per l'espressione della variabile sarà null. (Perché, cos'altro dovrebbe fare PHP? Ogni espressione deve dare un certo valore.)
  • Una variabile con null assegnata è comunque una variabile perfettamente normale.

18
Nota che se $whateverpunta a un oggetto, $whatever = nullsovrascrive il puntatore, non l'oggetto stesso, quindi agisce sostanzialmente come unset().
Gras Double

1
@VonC: la citazione non impostata su php.net a cui ti riferisci non esiste più.
Jürgen Thelen,

@ JürgenThelen vero, ma il contenuto di quella vecchia risposta sembra ancora pertinente, no?
VonC,

1
@VonC: Sicuramente. Non sono sicuro di "Non sono necessari cicli di CPU" e "prima che ... memoria esaurita" inneschi la garbage collection. Vedi stackoverflow.com/q/20230626/693207 . Forse puoi far luce?
Jürgen Thelen,

1
@Omar Ho modificato la risposta: la pagina man non impostata del 2009 (ho collegato alla versione 2009) include una sezione che non è più presente nella versione corrente di quella stessa pagina.
VonC,

48

unsetnon è in realtà una funzione, ma un costrutto linguistico . Non è più una chiamata di funzione di una returno una include.

A parte i problemi di prestazioni, l'utilizzo unsetrende l' intento del codice molto più chiaro.


Ecco perché li ho sempre usati, personalmente ho pensato che fossero migliori di $ var = null. A proposito, ho sempre usato NULL maiuscoli ... ma ora non so perché?
alex,

1
@VonC: Sì, l'ho immaginato, ma perché puoi usare il minuscolo vero, falso e nullo?
alex,

3
@alex, puoi farlo con unset. Ad esempio "$ test = 4; (unset) $ test;" - strano ma vero, e restituisce il valore di $ test prima di annullarlo. Indipendentemente da ciò, il manuale di PHP conferma che si tratta di un costrutto linguistico.
thomasrutter,

5
@alex: PSR-2 richiede lettere minuscole per tutte le parole chiave.
Tgr

2
@alex - Le parole chiave PHP non fanno distinzione tra maiuscole e minuscole; potresti anche scrivere unsetcome UnSeT, per esempio. La comunità ha optato per le minuscole come una questione di stile, ma altri involucri funzionano ancora.
Mark Reed,

35

Effettuando unset () su una variabile, hai essenzialmente contrassegnato la variabile per "garbage collection" (PHP non ne ha davvero una, ma per esempio) quindi la memoria non è immediatamente disponibile. La variabile non contiene più i dati, ma lo stack rimane di dimensioni maggiori. L'esecuzione del metodo null elimina i dati e riduce quasi immediatamente la memoria dello stack.

Questo è stato per esperienza personale e anche per altri. Vedi i commenti della funzione unset () qui .

Personalmente uso unset () tra le iterazioni in un ciclo in modo da non dover avere il ritardo dello stack di dimensioni yo-yo. I dati sono spariti, ma l'impronta rimane. Alla successiva iterazione, la memoria è già occupata da php e quindi, più rapidamente per inizializzare la variabile successiva.


15
L'impostazione di qualcosa su NULL può essere di beneficio se la memoria richiesta per contenere il valore NULL è inferiore a quella richiesta per contenere qualunque valore fosse precedentemente detenuto. Ad esempio, una stringa lunga. Se la stringa non era una costante e il suo conteggio dei riferimenti scende a zero, è necessario liberare quella memoria. Unset è più pulito - non mantiene più un riferimento. Devi aspettare la garbage collection, ma è sicuro trattarlo come se non occupasse memoria, perché una condizione di memoria insufficiente attiverà la garbage collection.
thomasrutter,

non possiamo usare entrambi? uguale a null e quindi non impostato?
Nabeel Khan,

2
@NabeelKhan Suggerirei di usare unset () all'interno dei loop e quindi annullarlo quando esci dal loop. Altrimenti, c'è un impatto sulle prestazioni nel fare entrambe le cose all'interno del loop. Se non stai usando i loop, annulla semplicemente come fa già la logica unset () dietro le quinte.
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";
?>

Per questo sembra che "= null" sia più veloce.

Risultati PHP 5.4:

  • impiegato 0,88389301300049 secondi
  • impiegato 2.1757180690765 secondi

Risultati PHP 5.3:

  • impiegato 1.7235369682312 secondi
  • ha impiegato 2.9490959644318 secondi

Risultati PHP 5.2:

  • ha impiegato 3.0069220066071 secondi
  • impiegato 4.7002630233765 secondi

Risultati di PHP 5.1:

  • ha impiegato 2.6272349357605 secondi
  • impiegato 5.0403649806976 secondi

Le cose iniziano a sembrare diverse con PHP 5.0 e 4.4.

5.0:

  • impiegato 10.038941144943 secondi
  • impiegato 7.0874409675598 secondi

4.4:

  • impiegato 7.5352551937103 secondi
  • ha impiegato 6,6245851516724 secondi

Tieni presente che microtime (true) non funziona in PHP 4.4, quindi ho dovuto usare l'esempio microtime_float fornito in php.net/microtime / Esempio n. 1.


7
Penso che il tuo test sia difettoso. Il primo loop è una semplice riassegnazione e il secondo loop distrugge e ricrea lo stesso simbolo. Se il test viene ripetuto con un array unsetè più veloce. Ho un test che in seguito verifica l'esistenza nel unsetcaso. In quel test impostarlo su nullè leggermente più veloce. Test: pastebin.com/fUe57C51
Knyri

4
@ansur, chiama sempre gc_collect_cyclesprima di avviare il timer per ottenere risultati più precisi.
Pacerier,

@knyri puoi per favore collegarti a quello?
Nabeel Khan,

@NabeelKhan Non ho più i risultati di quel test; ma c'è un link al codice di prova nel mio commento precedente.
Knyri,

19

Fa la differenza con gli elementi dell'array.

Considera questo esempio

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

Qui esiste ancora la chiave "test". Tuttavia, in questo esempio

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

la chiave non esiste più.


18

Funziona in modo diverso per le variabili copiate per riferimento:

$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

5
Ho programmato php alcuni anni ormai e non ho mai visto "&" riguardo al riferimento alla var originale. Grazie + 1 :)
Chris,

1
$ a = 78; $ b = $ a; unset ($ a); var_dump ($ b); // 78; var_dump ($ a); // Variabile non
definita

13

Per quanto riguarda gli oggetti, specialmente nello scenario di caricamento lento, si dovrebbe considerare che Garbage Collector è in esecuzione in cicli di CPU inattivi, quindi presumendo che si verifichino problemi quando molti oggetti caricano una piccola penalità di tempo risolverà la liberazione della memoria.

Utilizzare time_nanosleep per abilitare GC a raccogliere memoria. È consigliabile impostare la variabile su null.

Testato sul server di produzione, il lavoro originariamente consumava 50 MB e quindi veniva interrotto. Dopo l'utilizzo del nanosleep 14MB è stato un consumo costante di memoria.

Si dovrebbe dire che questo dipende dal comportamento del GC che può cambiare da versione PHP a versione. Ma funziona bene su PHP 5.3.

per esempio. questo esempio (codice preso dal feed 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);
    ...

3

Ne dubito ancora, ma l'ho provato nel mio script e sto usando xdebug per sapere come influenzerà l'utilizzo della memoria della mia app. Lo script è impostato sulla mia funzione in questo modo:

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;
}

E aggiungo unset appena prima del returncodice e mi dà: 160200 quindi provo a cambiarlo con $sql = NULLe mi dà: 160224 :)

Ma c'è qualcosa di unico in questo confronto quando non sto usando unset () o NULL, xdebug mi dà 160144 come utilizzo della memoria

Quindi, penso che dare la linea per usare unset () o NULL aggiungerà il processo alla tua applicazione e sarà meglio mantenere l'origine con il tuo codice e ridurre la variabile che stai usando nel modo più efficace possibile.

Correggimi se sbaglio, grazie


Penso che mentre stai restituendo l'elemento $ data [0], viene fatto riferimento all'intero array / ma è solo l'ipotesi. Prova a copiare $ data [0] nella variabile locale, imposta l'array su null e restituisce la variabile locale. Un buon background è qui tuxradar.com/practicalphp/18/1/11 e ofc. php.net/manual/en/features.gc.php
OSP

2

Ho creato un nuovo test delle prestazioni per unsete =null, poiché, come menzionato nei commenti, qui scritto ha un errore (la ricostruzione degli elementi). Ho usato le matrici, come vedi ora non importava.

<?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>';

Ma posso solo testarlo su un server PHP 5.5.9, qui i risultati: - sono stati necessari 4.4571571350098 secondi - sono stati necessari 4.4425978660583 secondi

Preferisco unsetper motivi di leggibilità.


2

PHP 7 ha già lavorato su tali problemi di gestione della memoria e sul suo utilizzo ridotto fino al minimo.

<?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";

?>

Uscita PHP 7.1:

impiegato 0.16778993606567 secondi impiegato 0.16630101203918 secondi


1

unsetil codice se non liberare la memoria immediata è ancora molto utile e sarebbe una buona pratica farlo ogni volta che passiamo i passaggi del codice prima di uscire da un metodo. prendi nota che non si tratta di liberare memoria immediata. la memoria immediata è per la CPU, che dire della memoria secondaria che è la RAM.

e questo affronta anche la prevenzione delle perdite di memoria.

si prega di consultare questo link http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

sto usando unset da molto tempo ormai.

una migliore pratica come questa nel codice per annullare istantaneamente tutte le variabili che sono già state utilizzate come array.

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

e just unset($data);per liberare tutto l'utilizzo variabile.

si prega di consultare l'argomento correlato da annullare

Quanto è importante disinserire le variabili in PHP?

[Bug]


1

Per la cronaca, ed escluso il tempo necessario:

<?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"; 

Ritorna

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

Conclusione, memoria libera sia nulla che non impostata come previsto (non solo alla fine dell'esecuzione). Inoltre, la riassegnazione di una variabile mantiene il valore due volte ad un certo punto (520216 contro 438352)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.