Garbage Collection zur Script Laufzeit ?
Marko
- php
Hallo,
gibt es in PHP eine Möglichkeit Objekte vor dem Scriptende aus dem Speicher zu
entfernen ?
Ich stosse gerade auf das Problem, dass ein Script eine komplette, ziemlich grosse Webseite aus einer Datenbank generiert. Dieser Prozess läuft sehr selten, also ist Zeit und Performance kein Problem. Aber mittlerweile musste ich das Memory Limit auf 30MB hochschrauben, weil während der Generierung sich immer mehr Müll im Speicher sammelt.
Eine Aufteilung in mehrere Schritte, die durch getrennte Requests ausgelöst werden wäre zwar möglich, aber umständlich.
Falls PHP4 nicht damit dienen kann, wie sieht es denn in PHP5 mit Garbage Collection aus ?
Danke und Gruss
Marko
Halihallo Marko
gibt es in PHP eine Möglichkeit Objekte vor dem Scriptende aus dem Speicher zu
entfernen ?
Ja. http://www.php.net/unset gibt die Variable für Garbage
Collecting frei.
Viele Grüsse
Philipp
Halihallo Marko
Ja. http://www.php.net/unset gibt die Variable für Garbage
Collecting frei.
Äm. Sie wird zwar für das Collecting freigegeben, aber das heisst
noch lange nicht, dass der Speicher an das OS zurückgegeben wird.
Ich weiss nicht genau, wie PHP dies intern handhabt, aber PHP wird
wohl auch ein internes Memory Management haben und hält sich den
Speicher vielleicht noch zurück...
BTW: Überschreibst du einen Variableninhalt, der nirgendwo sonst
referenziert ist (über eine Referenz), wird der Inhalt der Variablen
auch für das Garbage Collecting freigegeben. Nur die Variable selber
ist immer noch definiert (mit dem neuen Wert).
Viele Grüsse
Philipp
Hi!
gibt es in PHP eine Möglichkeit Objekte vor dem Scriptende aus dem Speicher zu
entfernen ?
Lies mal was zu "Reference-Counting" durch: http://www.zend.com/zend/art/ref-count.php
Du musst vor allem darauf achten - gerade bei großen Objekten, dass Du die Objekte möglichst explizit als Referenz übergibst, so dass die großen Objekte nicht mehrfach im Speicher existieren.
Du kannst Variablen auch selber dereferenzieren, am besten mit
$var = NULL;
Ja. http://www.php.net/unset gibt die Variable für Garbage
Collecting frei.
ich sags ja - PHP`ler, da kannste Dich gegen wehren oder nicht ;-)
Grüße
Andreas
Halihallo Andreas bzw. eher Marko
gibt es in PHP eine Möglichkeit Objekte vor dem Scriptende aus dem Speicher zu
entfernen ?Lies mal was zu "Reference-Counting" durch: http://www.zend.com/zend/art/ref-count.php
Schön, dass PHP das "jetzt" auch kann :-)
Du musst vor allem darauf achten - gerade bei großen Objekten, dass Du die Objekte möglichst explizit als Referenz übergibst, so dass die großen Objekte nicht mehrfach im Speicher existieren.
Jep. Nur: (@Marko)
<?php
$t3 = Array(content => 'test');
$t = Array( name => $t3 );
unset($t3);
var_dump($t);
?>
Tja, auch wenn das "array" mit content=>'test' gelöscht wird
(genauer ist es eben nur die Variable $t3, der Speicher vom Array
ist davon weitgehend unbeeinflusst), erscheint es noch bei var_dump.
Warum? - Nun, $t3 erhöht den reference counter (des Arrays!) auf 1,
die Referenz name im "Array $t" auf 2. Über unset($t3) oder $t3=NULL
wird der reference counter des ersten Arrays auf 1 decrementiert
und somit ist das Array (nicht die Variable $t3) immer noch im
Speicher. Um wirklich für den Garbage Collector zu arbeiten,
müsste man also auch $t löschen, sodass auch $t3 freigegeben wird...
Mit anderen Worten: Alle Referenzen auf eine (anonyme!) Datenstruktur
müssen (entweder Rekursiv oder direkt) gelöscht werden. Merke: Eine
Variable ist ebenfalls eine Referenz auf eine anonyme Datenstruktur.
z.B. zeigt bei '$t="hello"', die Variable $t auf eine Instanz der
Datenstruktur String, welche mit 'hello' "gefüllt" ist und somit
bereits einen reference counter von 1 aufweist.
Huch, hoffe das war in etwa verständlich :-)
Aber lieber doch Andreas verlinktes Dokument lesen :-)
Ja. http://www.php.net/unset gibt die Variable für Garbage
Collecting frei.ich sags ja - PHP`ler, da kannste Dich gegen wehren oder nicht ;-)
We are the PHP'ler. Resistence is futile.
Until you speak Perl, I suppose! :-)
Viele Grüsse
Philipp
Hello,
bekannte Versäumnisse sind
Bei PHP 4.x werden leider auch immer noch diese Supervariablen $HTTP_... angelegt. Wenn man also nur die neuen benutzt, was ich empfehle, sollte man die anderen gleich am Scriptanfang freigeben. Verhindern kann man die Generierung leider erst in einer neueren Version (ich galube ab 5.x).
Müsstest mal schauen, ob auch immer noch $HTTP_SESSION_VARS angelegt wird. Das wird dann den meisten Platz fressen.
Liebe Grüße aus http://www.braunschweig.de
Tom
Vielen Dank für die Antworten erstmal, ich weiss jetzt wo ich suchen muss. Der durchschlagende Erfolg blieb mir zwar noch versagt, aber ich muss wohl das System auf Speicherfresser durchforsten. Da rächt sich, wenn man sich unter PHP noch nie Gedanken drüber gemacht hat.
Noch eine Frage, gibt es für PHP Werkzeuge, mit denen man untersuchen kann, welche Objekte wieviel Speicher verwenden ?
Gruss
Marko
Hallo!
Vielen Dank für die Antworten erstmal, ich weiss jetzt wo ich suchen muss. Der durchschlagende Erfolg blieb mir zwar noch versagt, aber ich muss wohl das System auf Speicherfresser durchforsten. Da rächt sich, wenn man sich unter PHP noch nie Gedanken drüber gemacht hat.
Noch eine Frage, gibt es für PHP Werkzeuge, mit denen man untersuchen kann, welche Objekte wieviel Speicher verwenden ?
Es kommt sehr auf den Quellcode an. Du solltest möglichst modular vorgehen, das heißt entsprechende Methoden/Objekte verwenden, und dabei nicht alles auf einmal machen. Wenn Du sagst Du generierst eine Webseite, drängt sich mir die Vermutung auf dass Du alle Elemente der Webseite erst im Speicher erzeugst, und am Ende auf die Platte schreibst. Du solltest nach Möglichkeit besser sequentiell vorgehen, das heißt wenn möglich Datei für Datei erzeugen und direkt auf die Platte schreiben, und dann die Resourcen freigeben, indem Du dieselben Variablen... verwendest, z.B. in einer Schleife (in dem Fall werden die nicht mehr benötigten Resourcen bei jedem Schleifendurchlauf freigegeben).
Dann übergebe gerade Objekte und große Arrays nach Möglichkeit immer als Referenz, wie gesagt.
Du solltest den von mir verlinkten Artikel genau lesen und verstehen, und dann Dein Script entsprechend umbauen indem Du das "Reference-Counting" und Aliase?!(Referenzen -> http://de3.php.net/manual/en/language.references.php) ausnutzt.
In gewissen Grenzen können Dir die Funktion http://de3.php.net/memory-get-usage und die Extension http://xdebug.org/ helfen speicherintensive Bereiche und Funktionen zu finden.
Grüße
Andreas
Hallo Andreas,
das System ist voll Objektorientiert und modular aufgebaut. Es wird auch Seite für Seite erzeugt. Ich erzeuge die Dynamischen Seiten dabei sequentiell, puffere die Bildschirmausgabe und schreibe sie in Dateien.
Das Problem liegt wohl in der Objektübergabe, ich übergebe bisher nicht mit Referenzen. Wobei ich noch nicht ganz verstehe, warum der Speicher nicht freigegeben wird. Nach der Generierung einer Seite müssten eigentlich alle Referenzen gelöscht sein.
Möglicherweise hängt es auch an der DOM Extension, die ja noch beta ist, die ich aber ziemlich stark benutze.
Ich werde mich mal genau in das reference-counting einlesen, aber vielleicht ist es am besten ich schraube jetzt erstmal den Speicher hoch, und warte auf die 5er PHP. Wahrscheinlich muss ich das System früher oder später eh portieren, und mit PHP 5 werden vielleicht einige Problem von selbst verschwinden (Objektübergabe als Referenz, und eine stabile DOM Implementierung).
Danke und Gruss
Marko
Hi!
das System ist voll Objektorientiert und modular aufgebaut. Es wird auch Seite für Seite erzeugt. Ich erzeuge die Dynamischen Seiten dabei sequentiell, puffere die Bildschirmausgabe und schreibe sie in Dateien.
Hm. Wie genau machst Du das?
Rufst Du nach jeder Datei ob_end_clean() auf?
Verwendest Du immer denselben Variablennamen für die Objekte?
Ich kann mir nicht vorstellen dass es an Referenzen liegt, udn es wird vermutlich nicht durch PHP5 behoben sein. An irgendeiner Stelle erzeugst Du immer neue Objekte, oder mehr Arrays, einen Array mit neuen Objekten oder schreibst immer mehr in den Ausgabepuffer... irgendsowas.
Sonst schreibe mal den Inhalt von $GLOBALS am Ende in eine Datei und guck wie groß die ist.
$fp = fopen ('globals.txt','w');
fwrite($fp,var_export($GLOBALS, TRUE));
fclose($fp);
Wenn die Datei riesig ist musst Du nur suchen wo da sinnlose Daten sind und dann das Script entsprechend ändern, wenn die Datei nicht besonders groß ist, würde ich auf den Ausgabe-Puffer tippen, vielleicht verwendest Du ob_clean(), was vielleicht irgendwelche Daten im Speicher lässt. Vielleicht versuche mal am Ende der Datei:
$i=0;
while (ob_end_clean()) {
$i++;
}
$fp = fopen ('ob.txt','w');
fwrite($fp,$i);
fclose($fp);
Was steht dann in der ob.txt für eine Zahl?
Grüße
Andreas
Hallo Andreas,
Rufst Du nach jeder Datei ob_end_clean() auf?
Ja, das wird in einer eigenen Klasse gemacht. Die Klasse habe ich schon in 3 anderen Projekten verwendet, daran liegt s nicht. Ich habe auch die Zählschliefe getestet, die Du vorgeschlagen hast, das Ergebnis ist 0.
Verwendest Du immer denselben Variablennamen für die Objekte?
Eigentlich schon, der Generator sitzt nur vor einem CMS System, das die Dateien erzeugt, für Verwaltungszwecke und bei der Entwicklung wird immer direkt das CMS aufgerufen. Der Generator simuliert mit einem "include" für jede Datei den Aufruf.
An diesem include in der Schleife kann es eigentlich auch nicht liegen, hab gerade nachgeschaut, es wird nur sehr wenig Code eingebunden, und die Variablen werden dann wiederverwendet. Es ist ein zentraler Controller der ein Serviceobjekt instantiert, eine Methode aufruft, und das Objekt am Ende wieder freigibt.
Globals habe ich auch gecheckt, steht nichts weltbewegendes drin. Ich denke es liegt doch daran, dass entweder Objekte überleben, die das nicht sollen, oder wahrscheinlicher die DOM Bäume werden nicht freigegeben.Vielleicht ist auch einfach das Problem, dass bei Bedarf mit require_once neuer Code hinzugelinkt wird, bei der Generierung werden nach und nach alle Klassen des Systems eingebunden, das sind schon so ca. 30 Klassen, vielleicht ist das einfach ne Menge Code.
Gruss
Marko
Hi!
Eigentlich schon, der Generator sitzt nur vor einem CMS System, das die Dateien erzeugt, für Verwaltungszwecke und bei der Entwicklung wird immer direkt das CMS aufgerufen. Der Generator simuliert mit einem "include" für jede Datei den Aufruf.
Naja, daran könnte es durchaus liegen. Bei include bringt das Freigeben nichts, da wird das Scipt um jede eingebundene Datei immer weiter vergrößert.
Mach mal vor dem jedem include sowas:
$GLOBALS['filesize'] += filesize($filename);
und ganz am Ende
echo $GLOBALS['filesize'] .' bytes';
Du solltest statt include() vielleicht mal
echo file_get_contents() verwenden, wenn das denn geht. Auch wenn das dann am Ende falsche Dateien liefert, würde ich es trotzdem einmal ürobieren, um zu sehen ob es überhaupt an include() liegt, was ich vermute.
Grüße
Andreas
Hi,
die Zählung ergibt 123525 also nur 120 Kb. echo file_get_contents() geht nicht, da der Code ja ausgeführt werden muss.
Gruss
Marko
Hi!
die Zählung ergibt 123525 also nur 120 Kb. echo file_get_contents() geht nicht, da der Code ja ausgeführt werden muss.
Ja, aber muss das den ausgeführt werden damit der Generator funktioniert? Ist ja total egal wenn in den erzeugten Dateien am Ende Qutsch steht, nur mal um zu testen.
Naja, ohne Kenntnis des kompletten Quellcodes weiß ich dann auch nicht mehr weiter, ich würde wie gesagt mal an mehreren Stellen den Speicherverbrauch testen, am besten mal xdebug installieren.
Oder beschneide das Script immer weiter in seiner Funkionalität, bis Du die Stelle findest die für den wahnsinnigen Speicherverbrauch verantwortlich ist. Und log alles mögliche mal mit...
Grüße
Andreas
Halihallo Marko
Ich stosse gerade auf das Problem, dass ein Script eine komplette, ziemlich grosse Webseite aus einer Datenbank generiert. Dieser Prozess läuft sehr selten, also ist Zeit und Performance kein Problem. Aber mittlerweile musste ich das Memory Limit auf 30MB hochschrauben, weil während der Generierung sich immer mehr Müll im Speicher sammelt.
Was passiert, wenn du das Memory Limit auf 10MB ansetzt? -
Fehlermeldung?
Denn: PHP konsumiert soviel, wie es darf, setzt du das Limit auf 30MB
scheut sich PHP nicht davor, auch 30MB zu benutzen. Setzt du es
jedoch auf 10MB, muss es nichtverwendeten Speicher an das System
zurückgeben bzw. diesen für anderes wieder gebrauchen.
Es ist ggf. genau falsch das Memory Limit höher anzusetzen, teste
mal.
Zudem: Wieviel Speicher braucht ein kleines "Hello World - PHP-
Script"? - Du wirst feststellen, dass 4GL Sprachen wie PHP und Perl
gigantischen "Overhead" produzieren.
Viele Grüsse
Philipp
Hallo Philipp,
Was passiert, wenn du das Memory Limit auf 10MB ansetzt? -
Fehlermeldung?
ja klar, dadurch bin ich doch überhaupt erst auf das Problem gestossen.
Gruss
Marko