Was ist besser daran, Speicher mit PHP freizugeben: unset () oder $ var = null

244

Mir ist klar, dass der zweite den Overhead eines Funktionsaufrufs vermeidet ( Update ist eigentlich ein Sprachkonstrukt), aber es wäre interessant zu wissen, ob einer besser ist als der andere. Ich habe den unset()größten Teil meiner Codierung verwendet, aber ich habe kürzlich einige seriöse Klassen aus dem Internet durchgesehen, die $var = nullstattdessen verwendet werden.

Gibt es eine bevorzugte und was ist die Begründung?

Alex
quelle

Antworten:

234

Es wurde auf der Seite des nicht eingestellten Handbuchs im Jahr 2009 erwähnt :

unset()macht genau das, was sein Name sagt - eine Variable deaktivieren. Es erzwingt keine sofortige Speicherfreigabe. Der Garbage Collector von PHP wird dies tun, wenn es passt - absichtlich, sobald diese CPU-Zyklen ohnehin nicht benötigt werden oder erst, wenn dem Skript der Speicherplatz ausgeht, was zuerst passiert.

Wenn Sie dies tun $whatever = null;, schreiben Sie die Daten der Variablen neu. Möglicherweise wird Speicher schneller freigegeben / verkleinert, aber möglicherweise werden CPU-Zyklen aus dem Code gestohlen, der sie wirklich früher benötigt, was zu einer längeren Gesamtausführungszeit führt.

(Seit 2013 enthält diese unsetManpage diesen Abschnitt nicht mehr.)

Beachten Sie, dass bis php5.3, wenn Sie zwei Objekte in Zirkelreferenz haben , z. B. in einer Eltern-Kind-Beziehung, der Aufruf von unset () für das übergeordnete Objekt den für die übergeordnete Referenz im untergeordneten Objekt verwendeten Speicher nicht freigibt . (Der Speicher wird auch nicht freigegeben, wenn das übergeordnete Objekt durch Müll gesammelt wird.) ( Fehler 33595 )


Die Frage " Unterschied zwischen nicht gesetzt und = null " beschreibt einige Unterschiede:


unset($a)wird auch $aaus der Symboltabelle entfernt; beispielsweise:

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

Ausgänge:

Notice: Undefined variable: a in xxx
NULL

Aber wann $a = nullwird verwendet:

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

NULL

Es scheint, dass dies $a = nulletwas schneller ist als das unset()Gegenstück: Das Aktualisieren eines Symboltabelleneintrags scheint schneller zu sein als das Entfernen.


  • Wenn Sie versuchen, eine nicht vorhandene ( unset) Variable zu verwenden, wird ein Fehler ausgelöst und der Wert für den Variablenausdruck ist null. (Denn was sollte PHP sonst noch tun? Jeder Ausdruck muss einen Wert ergeben.)
  • Eine Variable, der Null zugewiesen ist, ist jedoch immer noch eine völlig normale Variable.
VonC
quelle
18
Beachten Sie, dass, wenn $whateverauf ein Objekt gezeigt wird, $whatever = nullder Zeiger und nicht das Objekt selbst überschrieben wird, sodass er im Wesentlichen genauso funktioniert wie unset().
Gras Double
1
@VonC: Das nicht gesetzte Zitat auf php.net, auf das Sie sich beziehen, existiert nicht mehr.
Jürgen Thelen
@ JürgenThelen stimmt, aber der Inhalt dieser alten Antwort scheint immer noch relevant zu sein, nein?
VonC
1
@VonC: Auf jeden Fall. Ich bin mir nur nicht sicher, ob "CPU-Zyklen nicht benötigt werden" und "vor ... nicht genügend Speicher" die Speicherbereinigung auslöst. Siehe stackoverflow.com/q/20230626/693207 . Vielleicht kannst du etwas Licht ins Dunkel bringen?
Jürgen Thelen
1
@Omar Ich habe die Antwort bearbeitet: Die nicht gesetzte Manpage von 2009 (ich habe auf die Version 2009 verlinkt) enthält einen Abschnitt, der in der aktuellen Version derselben Seite nicht mehr vorhanden ist.
VonC
48

unsetist eigentlich keine Funktion, sondern ein Sprachkonstrukt . Es ist nicht mehr ein Funktionsaufruf als ein returnoder ein include.

Abgesehen von Leistungsproblemen unsetmacht die Verwendung die Absicht Ihres Codes viel klarer.

Alex Barrett
quelle
Deshalb habe ich sie immer benutzt, persönlich dachte ich, sie sehen besser aus als $ var = null. Übrigens habe ich immer NULL-Vollkappen verwendet ... aber jetzt weiß ich nicht warum?
alex
1
@VonC: Ja, das habe ich mir gedacht, aber warum kannst du Kleinbuchstaben wahr, falsch und null verwenden?
alex
3
@alex, das kannst du mit unset machen. Zum Beispiel "$ test = 4; (nicht gesetzt) ​​$ test;" - seltsam, aber wahr, und es gibt den Wert von $ test zurück, bevor es deaktiviert wird. Unabhängig davon bestätigt das PHP-Handbuch, dass es sich um ein Sprachkonstrukt handelt.
Thomasrutter
5
@alex: PSR-2 erfordert für alle Schlüsselwörter Kleinbuchstaben.
Tgr
2
@alex - Bei PHP-Schlüsselwörtern wird die Groß- und Kleinschreibung nicht berücksichtigt. Sie könnten zum Beispiel auch buchstabieren unsetals UnSeT. Die Community hat sich aus Stilgründen auf Kleinbuchstaben festgelegt, aber andere Gehäuse funktionieren immer noch.
Mark Reed
35

Indem Sie ein unset () für eine Variable ausführen, haben Sie die Variable im Wesentlichen für 'Garbage Collection' markiert (PHP hat nicht wirklich eine, aber zum Beispiel), sodass der Speicher nicht sofort verfügbar ist. Die Variable enthält die Daten nicht mehr, aber der Stapel bleibt größer. Wenn Sie die Null-Methode ausführen, werden die Daten gelöscht und der Stapelspeicher fast sofort verkleinert.

Dies war aus persönlicher Erfahrung und auch aus anderen. Siehe die Kommentare der Funktion unset () hier .

Ich persönlich verwende unset () zwischen Iterationen in einer Schleife, damit ich nicht die Verzögerung haben muss, mit der der Stapel von Jo-Jo-Größe ist. Die Daten sind weg, aber der Footprint bleibt erhalten. Bei der nächsten Iteration wird der Speicher bereits von PHP belegt und somit die nächste Variable schneller initialisiert.

William Holroyd
quelle
15
Das Setzen von etwas auf NULL kann von Vorteil sein, wenn der Speicher, der zum Speichern des Werts NULL erforderlich ist, kleiner ist als der, der zum Speichern des zuvor enthaltenen Werts erforderlich ist. Zum Beispiel eine lange Zeichenfolge. Wenn die Zeichenfolge keine Konstante war und ihr Referenzzähler auf Null fällt, sollte dieser Speicher freigegeben werden. Unset ist sauberer - es wird keine Referenz mehr beibehalten. Sie müssen auf die Speicherbereinigung warten, aber es ist sicher, dass sie keinen Speicher belegt, da ein Zustand mit wenig Speicher die Speicherbereinigung auslöst.
Thomasrutter
können wir nicht beide verwenden? gleich null und dann nicht gesetzt?
Nabeel Khan
2
@NabeelKhan Ich würde vorschlagen, unset () in Schleifen zu verwenden und sie dann aufzuheben, wenn Sie die Schleife verlassen. Andernfalls wirkt sich dies auf die Leistung aus, wenn beide Aufgaben innerhalb der Schleife ausgeführt werden. Wenn Sie keine Schleifen verwenden, heben Sie diese einfach auf, da die Logik unset () bereits hinter den Kulissen ausgeführt wird.
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";
?>

Demnach scheint "= null" schneller zu sein.

PHP 5.4 Ergebnisse:

  • dauerte 0,88389301300049 Sekunden
  • dauerte 2,1757180690765 Sekunden

PHP 5.3 Ergebnisse:

  • dauerte 1,7235369682312 Sekunden
  • dauerte 2,9490959644318 Sekunden

PHP 5.2 Ergebnisse:

  • dauerte 3,0069220066071 Sekunden
  • dauerte 4.7002630233765 Sekunden

PHP 5.1 Ergebnisse:

  • dauerte 2,6272349357605 Sekunden
  • dauerte 5,0403649806976 Sekunden

Mit PHP 5.0 und 4.4 sieht es anders aus.

5.0:

  • dauerte 10.038941144943 Sekunden
  • dauerte 7.0874409675598 Sekunden

4.4:

  • dauerte 7,5352551937103 Sekunden
  • dauerte 6,6245851516724 Sekunden

Denken Sie daran, dass microtime (true) in PHP 4.4 nicht funktioniert, daher musste ich das Beispiel microtime_float verwenden, das in php.net/microtime / Beispiel 1 angegeben ist.


quelle
7
Ich denke, Ihr Test ist fehlerhaft. Die erste Schleife ist eine einfache Neuzuweisung, und die zweite Schleife zerstört und erstellt dasselbe Symbol neu. Wenn der Test mit einem Array wiederholt wird, unsetist schneller. Ich habe einen Test, der später die Existenz des unsetFalles überprüft . In dieser Testeinstellung nullist es geringfügig schneller. Test: pastebin.com/fUe57C51
Knyri
4
@ansur, rufen Sie immer an, gc_collect_cyclesbevor Sie den Timer starten, um genauere Ergebnisse zu erhalten.
Pacerier
@knyri kannst du bitte darauf verlinken?
Nabeel Khan
@NabeelKhan Ich habe nicht mehr die Ergebnisse dieses Tests; In meinem vorherigen Kommentar befindet sich jedoch ein Link zum Testcode.
Knyri
19

Bei Array-Elementen macht dies einen Unterschied.

Betrachten Sie dieses Beispiel

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

Hier existiert noch der Schlüssel 'Test'. In diesem Beispiel jedoch

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

Der Schlüssel existiert nicht mehr.

Auris
quelle
18

Bei Variablen, die als Referenz kopiert wurden, funktioniert dies anders:

$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
quelle
5
Ich habe PHP jetzt ein paar Jahre codiert und nie "&" über das Referenzieren der ursprünglichen var gesehen. Danke + 1 :)
Chris
1
$ a = 78; $ b = $ a; nicht gesetzt ($ a); var_dump ($ b); // 78; var_dump ($ a); // Undefinierte Variable: a
zloctb
13

In Bezug auf Objekte, insbesondere im Lazy-Load-Szenario, sollte berücksichtigt werden, dass der Garbage Collector in Leerlauf-CPU-Zyklen ausgeführt wird. Wenn Sie also Probleme haben, wenn viele Objekte eine kleine Zeitstrafe laden, wird die Speicherfreigabe behoben.

Verwenden Sie time_nanosleep, um GC das Sammeln von Speicher zu ermöglichen. Das Setzen der Variablen auf Null ist wünschenswert.

Auf dem Produktionsserver getestet, verbrauchte der Job ursprünglich 50 MB und wurde dann angehalten. Nach Verwendung von Nanosleep waren 14 MB konstanter Speicherverbrauch.

Man sollte sagen, dass dies vom GC-Verhalten abhängt, das sich von PHP-Version zu Version ändern kann. Aber es funktioniert gut mit PHP 5.3.

z.B. dieses Beispiel (Code aus VirtueMart2 Google Feed)

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
quelle
3

Ich bezweifle dies immer noch, aber ich habe es in meinem Skript versucht und verwende xdebug, um zu wissen, wie sich dies auf die Nutzung des App-Speichers auswirkt. Das Skript ist auf meine Funktion wie folgt eingestellt:

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

Und ich füge unset kurz vor dem returnCode hinzu und es gibt mir: 160200 dann versuche ich es mit zu ändern $sql = NULLund es gibt mir: 160224 :)

Aber es gibt etwas Einzigartiges an diesem Vergleich, wenn ich nicht unset () oder NULL verwende. Xdebug gibt mir 160144 als Speichernutzung

Ich denke also, wenn Sie die Zeile unset () oder NULL verwenden, wird Ihre Anwendung um einen Prozess erweitert, und es ist besser, den Ursprung Ihres Codes beizubehalten und die von Ihnen verwendete Variable so effektiv wie möglich zu verringern.

Korrigieren Sie mich, wenn ich falsch liege, danke

Anggie Aziz
quelle
Ich denke, während Sie $ data [0] zurückgeben, wird auf das gesamte Array verwiesen / aber es ist nur die Hypothese. Versuchen Sie, $ data [0] in die lokale Variable zu kopieren, setzen Sie das Array auf null und geben Sie die lokale Variable zurück. Guter Hintergrund ist hier tuxradar.com/practicalphp/18/1/11 und ofc. php.net/manual/en/features.gc.php
OSP
2

Ich habe einen neuen Leistungstest für unsetund erstellt =null, da, wie in den Kommentaren erwähnt, der hier geschriebene einen Fehler aufweist (die Neuerstellung der Elemente). Ich habe Arrays verwendet, wie Sie sehen, war das jetzt egal.

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

Aber ich kann es nur auf einem PHP 5.5.9 Server testen, hier die Ergebnisse: - dauerte 4.4571571350098 Sekunden - dauerte 4.4425978660583 Sekunden

Ich bevorzuge unsetaus Gründen der Lesbarkeit.

Michael B.
quelle
2

PHP 7 arbeitet bereits an solchen Speicherverwaltungsproblemen und deren reduzierter bis minimaler Nutzung.

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

dauerte 0,16778993606567 Sekunden dauerte 0,16630101203918 Sekunden

Swapnil
quelle
1

unsetCode, wenn nicht sofortiger Speicher freigegeben wird, ist immer noch sehr hilfreich und wird empfohlen, dies jedes Mal zu tun, wenn wir Codeschritte weitergeben, bevor wir eine Methode beenden. Beachten Sie, dass es nicht darum geht, sofortigen Speicher freizugeben. Der unmittelbare Speicher ist für die CPU, was ist mit dem sekundären Speicher, der RAM ist.

und dies betrifft auch die Verhinderung von Speicherlecks.

Bitte sehen Sie diesen Link http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

Ich benutze unset schon lange.

Eine bessere Vorgehensweise wie diese im Code, um alle Variablen, die bereits als Array verwendet wurden, sofort zu deaktivieren.

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

und just unset($data);um alle variablen Nutzung freizugeben.

siehe verwandtes Thema zu deaktivieren

Wie wichtig ist es, Variablen in PHP zu deaktivieren?

[Fehler]

Null8
quelle
1

Für die Aufzeichnung und ohne die Zeit, die es dauert:

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

Es kehrt zurück

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

Schlussfolgerung: Sowohl null als auch nicht festgelegter freier Speicher wie erwartet (nicht nur am Ende der Ausführung). Wenn Sie eine Variable neu zuweisen, wird der Wert zu einem bestimmten Zeitpunkt zweimal gespeichert (520216 gegenüber 438352).

Magallane
quelle