Quoi de mieux pour libérer de la mémoire avec PHP: unset () ou $ var = null


244

Je me rends compte que le second évite la surcharge d'un appel de fonction (la mise à jour , est en fait une construction de langage), mais il serait intéressant de savoir si l'un est meilleur que l'autre. J'ai utilisé unset()pour la plupart de mon codage, mais j'ai récemment parcouru quelques classes respectables trouvées sur le net qui utilisent à la $var = nullplace.

Y en a-t-il un préféré, et quel est le raisonnement?

Réponses:


234

Il a été mentionné dans la page du manuel non défini en 2009 :

unset()fait exactement ce que son nom dit - désinstaller une variable. Il ne force pas la libération immédiate de la mémoire. Le garbage collector de PHP le fera quand bon lui semblera - par intention dès que ces cycles de CPU ne sont pas nécessaires de toute façon, ou aussi tard qu'avant, le script manquerait de mémoire, quoi qu'il arrive en premier.

Si vous le faites, $whatever = null;vous réécrivez les données des variables. Vous pouvez obtenir de la mémoire libérée / rétrécie plus rapidement, mais cela peut voler les cycles du processeur du code qui en a vraiment besoin plus tôt, ce qui entraîne un temps d'exécution global plus long.

(Depuis 2013, cette unsetpage de manuel n'inclut plus cette section)

Notez que jusqu'à php5.3, si vous avez deux objets en référence circulaire , comme dans une relation parent-enfant, appeler unset () sur l'objet parent ne libérera pas la mémoire utilisée pour la référence parent dans l'objet enfant. (La mémoire ne sera pas non plus libérée lorsque l'objet parent est récupéré.) ( Bogue 33595 )


La question " différence entre unset et = null " détaille quelques différences:


unset($a)supprime également $ade la table des symboles; par exemple:

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

Les sorties:

Notice: Undefined variable: a in xxx
NULL

Mais quand $a = nullest-il utilisé:

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

NULL

Il semble que $a = nullc'est un peu plus rapide que son unset()homologue: la mise à jour d'une entrée de table de symboles semble être plus rapide que sa suppression.


  • lorsque vous essayez d'utiliser un élément inexistant (unset variable ), une erreur sera déclenchée et la valeur de l'expression de variable sera nulle. (Parce que, quoi d'autre PHP devrait-il faire? Chaque expression doit donner une certaine valeur.)
  • Une variable avec null lui est cependant toujours une variable parfaitement normale.

18
Notez que si $whateverpointe vers un objet, $whatever = nullremplace le pointeur, pas l'objet lui-même, il agit donc de la même manière que unset().
Gras Double

1
@VonC: la citation non réglée sur php.net à laquelle vous faites référence n'existe plus.
Jürgen Thelen

@ JürgenThelen vrai, mais le contenu de cette ancienne réponse semble toujours pertinent, non?
VonC

1
@VonC: Certainement. Je ne suis simplement pas sûr que "les cycles CPU ne soient pas nécessaires" et "avant .. que la mémoire soit insuffisante" déclenche la collecte des ordures. Voir stackoverflow.com/q/20230626/693207 . Vous pouvez peut-être faire la lumière?
Jürgen Thelen

1
@Omar J'ai édité la réponse: La page de manuel non définie de 2009 (que j'ai liée à la version 2009) comprend une section qui n'est plus présente dans la version actuelle de cette même page.
VonC

48

unsetn'est pas en fait une fonction, mais une construction de langage . Ce n'est pas plus un appel de fonction qu'un returnou un include.

Mis à part les problèmes de performances, l'utilisation unsetrend l' intention de votre code beaucoup plus claire.


C'est pourquoi je les ai toujours utilisés, personnellement, je pensais qu'ils avaient l'air mieux que $ var = null. Au fait, j'ai toujours utilisé des majuscules NULL ... mais maintenant je ne sais pas pourquoi?
alex

1
@VonC: Oui, je l'ai pensé, mais pourquoi pouvez-vous utiliser true, false et null en minuscules?
alex

3
@alex, vous pouvez en quelque sorte le faire avec unset. Par exemple, "$ test = 4; (unset) $ test;" - étrange mais vrai, et il retourne la valeur de $ test avant de le désactiver. Quoi qu'il en soit, le manuel PHP confirme qu'il s'agit d'une construction de langage.
thomasrutter

5
@alex: PSR-2 nécessite des minuscules pour tous les mots clés.
Tgr

2
@alex - Les mots clés PHP ne respectent pas la casse; vous pouvez également épeler unsetcomme UnSeT, par exemple. La communauté a opté pour le tout en minuscule par style, mais d'autres boîtiers fonctionnent toujours.
Mark Reed

35

En faisant unset () sur une variable, vous avez essentiellement marqué la variable pour `` garbage collection '' (PHP n'en a pas vraiment, mais par exemple), donc la mémoire n'est pas immédiatement disponible. La variable ne contient plus les données, mais la pile reste à la plus grande taille. Faire la méthode null supprime les données et réduit la mémoire de la pile presque immédiatement.

Cela vient de l'expérience personnelle et d'autres. Voir les commentaires de la fonction unset () ici .

Personnellement, j'utilise unset () entre les itérations dans une boucle afin que je n'aie pas à avoir le retard de la taille de la pile en yo-yo. Les données ont disparu, mais l'empreinte demeure. À la prochaine itération, la mémoire est déjà prise par php et donc, plus rapide pour initialiser la variable suivante.


15
La définition de quelque chose sur NULL peut être avantageuse si la mémoire requise pour contenir la valeur NULL est inférieure à celle requise pour contenir la valeur qu'elle contenait précédemment. Par exemple, une longue chaîne. Si la chaîne n'était pas une constante et que son nombre de références tombe à zéro, alors cette mémoire doit être libérée. Unset est plus propre - il ne conserve plus de référence. Vous devez attendre le garbage collection, mais il est sûr de le traiter comme n'occupant pas de mémoire, car une condition de mémoire insuffisante déclenchera le garbage collection.
thomasrutter

ne pouvons-nous pas utiliser les deux? égal à null, puis non défini?
Nabeel Khan

2
@NabeelKhan Je suggère d'utiliser unset () à l'intérieur des boucles, puis de l'annuler lorsque vous quittez la boucle. Sinon, il y a un impact sur les performances à faire les deux à l'intérieur de la boucle. Si vous n'utilisez pas de boucles, alors annulez simplement car cela fait déjà la logique unset () dans les coulisses.
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";
?>

Par cela, il semble que "= null" est plus rapide.

Résultats PHP 5.4:

  • a pris 0,88389301300049 secondes
  • a pris 2.1757180690765 secondes

Résultats PHP 5.3:

  • a pris 1.7235369682312 secondes
  • a pris 2,9490959644318 secondes

Résultats PHP 5.2:

  • a pris 3.0069220066071 secondes
  • a pris 4.7002630233765 secondes

Résultats PHP 5.1:

  • a pris 2,6272349357605 secondes
  • a pris 5,0403649806976 secondes

Les choses commencent à être différentes avec PHP 5.0 et 4.4.

5.0:

  • a pris 10,038941144943 secondes
  • a pris 7.0874409675598 secondes

4.4:

  • a pris 7,5352551937103 secondes
  • a pris 6,6245851516724 secondes

Gardez à l'esprit que microtime (true) ne fonctionne pas en PHP 4.4, j'ai donc dû utiliser l'exemple microtime_float donné dans php.net/microtime / Example # 1.


7
Je pense que votre test est imparfait. La première boucle est une simple réaffectation et la deuxième boucle détruit et recrée le même symbole. Si le test est refait avec un tableau, unsetc'est plus rapide. J'ai un test qui vérifie plus tard l'existence de l' unsetaffaire. Dans ce test, il nullest légèrement plus rapide. Test: pastebin.com/fUe57C51
Knyri

4
@ansur, appelez toujours gc_collect_cyclesavant de démarrer la minuterie pour obtenir des résultats plus précis.
Pacerier

@knyri pouvez-vous s'il vous plaît lier à cela?
Nabeel Khan

@NabeelKhan Je n'ai plus les résultats de ce test; mais il y a un lien vers le code de test dans mon commentaire précédent.
Knyri

19

Cela fait une différence avec les éléments du tableau.

Considérez cet exemple

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

Ici, le «test» clé existe toujours. Cependant, dans cet exemple

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

la clé n'existe plus.


18

Il fonctionne de manière différente pour les variables copiées par référence:

$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
J'ai codé php depuis quelques années et je n'ai jamais vu "&" sur le référencement de la var d'origine. Merci + 1 :)
Chris

1
$ a = 78; $ b = $ a; non réglé ($ a); var_dump ($ b); // 78; var_dump ($ a); // Variable non
définie

13

En ce qui concerne les objets, en particulier dans le scénario de chargement paresseux, il convient de considérer que le garbage collector s'exécute dans des cycles CPU inactifs, donc en supposant que vous rencontrez des problèmes lorsque beaucoup d'objets chargent une petite pénalité de temps résoudra la libération de mémoire.

Utilisez time_nanosleep pour permettre au GC de collecter de la mémoire. Il est souhaitable de définir la variable sur null.

Testé sur le serveur de production, le travail consommait à l'origine 50 Mo, puis a été interrompu. Après avoir utilisé le nanosommeil, 14 Mo étaient une consommation de mémoire constante.

Il faut dire que cela dépend du comportement du GC qui peut changer d'une version PHP à l'autre. Mais cela fonctionne bien sur PHP 5.3.

par exemple. cet exemple (code pris sous forme de flux 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

J'en doute encore, mais je l'ai essayé avec mon script et j'utilise xdebug pour savoir comment cela affectera l'utilisation de la mémoire de mon application. Le script est défini sur ma fonction comme ceci:

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

Et j'ajoute unset juste avant le returncode et ça me donne: 160200 puis j'essaye de le changer avec $sql = NULLet ça me donne: 160224 :)

Mais il y a quelque chose d'unique sur ce comparatif quand je n'utilise pas unset () ou NULL, xdebug me donne 160144 comme utilisation de mémoire

Donc, je pense que donner la ligne pour utiliser unset () ou NULL ajoutera un processus à votre application et il sera préférable de conserver l'origine avec votre code et de diminuer la variable que vous utilisez aussi efficacement que possible.

Corrigez-moi si je me trompe, merci


Je pense que pendant que vous retournez l'élément $ data [0], tout le tableau est référencé / mais c'est juste l'hypothèse. Essayez de copier $ data [0] dans une variable locale, définissez le tableau sur null et renvoyez la variable locale. Un bon fond est ici tuxradar.com/practicalphp/18/1/11 et ofc. php.net/manual/en/features.gc.php
OSP

2

J'ai créé un nouveau test de performance pour unsetet =null, car comme mentionné dans les commentaires l'écrit ici a une erreur (la recréation des éléments). J'ai utilisé des tableaux, comme vous le voyez, cela n'avait plus d'importance maintenant.

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

Mais je ne peux le tester que sur un serveur PHP 5.5.9, voici les résultats: - a pris 4.4571571350098 secondes - a pris 4.4425978660583 secondes

Je préfère unsetpour des raisons de lisibilité.


2

PHP 7 est déjà travaillé sur ces problèmes de gestion de la mémoire et son utilisation réduite à minimale.

<?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:

a pris 0,16778993606567 secondes a pris 0,16630101203918 secondes


1

unsetle code s'il ne libère pas de mémoire immédiate est toujours très utile et serait une bonne pratique de le faire chaque fois que nous passons des étapes de code avant de quitter une méthode. prenez note qu'il ne s'agit pas de libérer de la mémoire immédiate. la mémoire immédiate est pour le CPU, qu'en est-il de la mémoire secondaire qui est la RAM.

et cela aborde également la prévention des fuites de mémoire.

veuillez consulter ce lien http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

j'utilise unset depuis longtemps maintenant.

une meilleure pratique comme celle-ci dans le code pour désinstaller instantanément toutes les variables qui ont déjà été utilisées comme tableau.

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

et just unset($data);pour libérer toute utilisation variable.

veuillez consulter le sujet connexe pour désactiver

Quelle est l'importance de supprimer les variables en PHP?

[punaise]


1

Pour mémoire, et en excluant le temps qu'il faut:

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

Il revient

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

Conclusion, à la fois la mémoire libre nulle et non définie comme prévu (pas seulement à la fin de l'exécution). De plus, la réaffectation d'une variable retient la valeur deux fois à un moment donné (520216 contre 438352)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.