Memory überschritten -> Warum?

Alice

Erfahrenes Mitglied
Hallo. :)

Ich habe heute zum ersten Mal mein PHP-Skript (welches Grafiken wie z.B. Verkehrszeichen erzeugt) in voller Qualität laufen lassen. Also mit den richtigen Grafiken in voller Größe.

Stellt euch ein Ortschild vor mit der Größe 8000x2000px. Leider wurde das Skript durch den Memory-Limit gestoppt. Erst nach ehtlichen "auskommentieren" von Funktionen und erhöhung des Speichers auf 128MB lief es durch.

Ich bin jetzt nach der Suche nach dem Problem.

Ich vermute es sind die zu Maße der Grafiken. Manche einzelnen Grafiken sind locker 500x2000px groß.

Gibt es eine Möglichkeit was ich machen könnte? Natürlich möchte ich NICHT kleinere Grafiken wegen den teilweise Fotorealistischen Details verwenden.

Was mich wundert ist das die ausgegebene Grafik nur ca. 200KB groß ist.

Das Srkipt löst den Fehler an einer interessanten Stelle im Skript aus. Denn an dieser Stelle werden Leerräume ausgefüllt mit einer Grafik die gerade einmal 1x2000px groß ist. Jedoch wird diese Grafik in einer Schleife hunderte male wiederholt bis der Leerraum gefüllt ist.
 
Poste doch mal den Quellcode von der Stelle.

Der hohe Speicherverbrauch muss aber nicht unbedingt mit dieser Stelle zusammenhängen, dort wird nur das Limit überschritten.
 
Ist ein ganz einfacher Code.

Ist eine Schleife die imagecreatfrompng ein paar hundert mal nacheinander ausführt. (XD)

Edit:

Die Standardmäßig Eingestellte Größe ist von meinem Hoster 67108864 bytes (64MB). Ist das zu wenig oder in ordnung?

Edit:

Ich vermute mal ich muss meine Grafiken bearbeiten. :rolleyes:

Ich habe den Memory-Limit manuell auf 512 MB eingestellt und das Skript läuft trotzdem in den Limitter. Das Skript ist aber absolut sauber programmiert. Es muss wohl an den riesen Grafiken liegen.
 
Zuletzt bearbeitet:
Naja, du kannst es relativ einfach ausrechnen: 8000 * 2000 * 3 + Bild-Header-Länge (Abhängig vom Bild-Typ), das sind schon mal mindestens etwa 4,8 MB. Wenn du das jetzt hundert mal laufen lässt, sind das 480 MB. Evtl machst du den Ressourcen nicht sauber (image_destroy()). Evtl. hilft auch unset() bei größeren Variablen. Aber ohne Sourcecode lässt sich das nicht sagen. Und offensichtlich ist dein Code nicht sauber programmiert, sonst würde das nicht passieren.

64MB genügen für dein Vorhaben alle mal, außerdem denke ich mal, dass das Verändern des Memory-Limits bestimmt vom Hoster unterbunden wurde. Benutzt du dafür http://www.php.net/manual/en/function.ini-set.php ?
 
Ich kann das Limit verändern. Ich habe eben 1024 MB eingestellt und das Skript läuft dann auch durch.

PHP:
@ini_set("memory_limit",'1024M'); // 1024 MB

Ohne das Limit zu verändern bekomme ich folgenden Fehler angezeigt:
Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 4 bytes) in ...

Sehe ich das falsch oder fehlen nur 4 Bytes?

Was den Fehler auslösen MUSS ist folgender Code (die Schleife)

PHP:
   $number = '200'; // Wird eigentlich ausgerechnet

   for($i=0; $i < $number; ++$i) {
       $img_size1 = getimagesize($pfad);
       $img_end[] = array('avapic' => imagecreatefrompng($pfad), 'sizex' => $img_size1[0],);
       $img_size2 = $img_size2 + $img_size1[0];
   }

Das Problem ist das diese Schleife mehrmals (verschiedene) ausgeführt wird.

PHP:
   $number1 = '300'; // Wird eigentlich ausgerechnet
   $number2 = '100'; // Wird eigentlich ausgerechnet
   $number3 = '300'; // Wird eigentlich ausgerechnet

   for($i=0; $i < $number1; ++$i) {
       $img_size1 = getimagesize($pfad1);
       $img_end[] = array('avapic' => imagecreatefrompng($pfad1), 'sizex' => $img_size1[0],);
       $img_size2 = $img_size2 + $img_size1[0];
   }

   for($i=0; $i < $number2; ++$i) {
       $img_size1 = getimagesize($pfad2);
       $img_end[] = array('avapic' => imagecreatefrompng($pfad2), 'sizex' => $img_size1[0],);
       $img_size2 = $img_size2 + $img_size1[0];
   }

   for($i=0; $i < $number3; ++$i) {
       $img_size1 = getimagesize($pfad3);
       $img_end[] = array('avapic' => imagecreatefrompng($pfad3), 'sizex' => $img_size1[0],);
       $img_size2 = $img_size2 + $img_size1[0];
   }

Wenn etwas nicht sauber programmiert ist, dann hier. Alles andere sind nur Berechnungen ohne das eine einzige Grafik geladen wird. Das läuft das Skript auch extrem schnell durch.
 
Das ist schon sehr unklug, was du da vor hast. Wenn sich dein Pfad nicht ändert, dann brauchst du doch auch nicht jedes mal das gleiche Bild in den Speicher laden. Speicher in deinem Array einfach die Ausmaße des Bildes und in einer weiteren Variablen den Zeiger auf die Bildresource. Damit lädst du jedes Bild nur einmal in den Speicher und nicht n mal.
 
Kannst du mir das bitte Anhand eines Codes zeigen?

Ich habe es mal so versucht wie ich dich verstanden habe, aber damit funktioniert es leider auch nicht. Also das Limit wird DEUTLICH überschritten.

PHP:
   $number1 = '1000'; // Wird eigentlich ausgerechnet

   $img_repeat =  imagecreatefrompng($pfad1);
   $img_size1 = imagesx($pfad1);

   for($i=0; $i < $number1; ++$i) {
       $img_end[] = array('avapic' => $img_repeat, 'sizex' => $img_size1);
       $img_size2 = $img_size2 + $img_size1;
   }

Die Grafik wird also NICHT NUR EINMAL geladen sondern halt 1000 mal.
 
Das ist mir als Anfänger zu hoch.

Könnt ihr mir nicht BITTE ein Code Beispiel zeigen? Ich habe doch extra einen Code gepostet (der funktioniert) um nicht ganz als "kopierer" hingestellt zu werden.
 
Es geht nicht darum, dir vorzuwerfen, du würdest etwas kopieren, sondern darum, das du verstehst, was du eigentlich treibst und warum der eine Weg sinnvoller ist als der andere ;-)

Also ich erklär mal ein bisschen die Interna, wie PHP mit Variablen und ihren Inhalten umgeht. PHP hat intern eine Tabelle (eine sog. Hash-Table), in der die Variablen (Namen) den Inhalten (Speicher-Adressen) zugeordnet sind. Wenn du jetzt eine neue Variable $a erzeugst und ihr den Wert von sagen wir mal 5 zuweißt, passieren folgende Dinge:

- Es wird Speicher allokiert (vom Betriebssystem angefordert), um den Wert 5 ablegen zu können (hier ist es gerade mal 1 Byte, denn das genügt für den Wert 5)
- Das kann jetzt schief gehen (weil kein Speicher mehr verfügbar ist, also das Limit erreicht), und dann kommt die dir bekannte Fehlermeldung
- Oder es geht gut und dann:
- In der Hash-Table wird ein neues Element eingefügt, bei dem der Variablen-Name eingetragen und die Speicher-Adresse auf den allokierten Speicherbereich.

Was passiert jetzt bei dir im Code?

- Du hast mittels imagecreatefrompng() so-und-so-viel Speicher allokiert (sagen wir mal 1 MB, weils sich einfach rechnen lässt)
- In der PHP-Variablen-Tabelle gibt es jetzt also einen Eintrag $img_repeat der zu der Speicher-Adresse gehört, an der die Bilddaten liegen.
- Jetzt durchläufst du eine Schleife und
- Erstellst innerhalb eines Arrays ein neues Element
- Das neue Element belegt natürlich auch einen Eintrag in der PHP-Variablen-Tabelle
- Das neue Element bekommt den Inhalt der Variablen $img_repeat, hier wird also Speicher erneut für die Bilddaten allokiert.
- Du hast jetzt also die Bilddaten doppelt im Speicher liegen.
- Durch weitere Durchläufe der Schleife verschlimmert sich das immer weiter.

Wie kann man das kopieren der Bilddaten im Speicher verhindern?

- Durch Referenzen!

Anhand deines Codes dürfte das dann so ablaufen:

PHP:
   $number1 = '1000'; // Wird eigentlich ausgerechnet

   $img_repeat =  imagecreatefrompng($pfad1);
   $img_size1 = imagesx($pfad1);

   for($i=0; $i < $number1; ++$i) {
       $img_end[] = array('avapic' => &$img_repeat, 'sizex' => $img_size1);
       $img_size2 = $img_size2 + $img_size1;
   }

Der Unterschied zwischen deinem und meinem Code ist jetzt das kaufmännische Und (&), was in der Array-Anweisung steht. Damit müsste der gleiche Speicher wie für $img_repeat für das Element in $img_end[n]['avapic'] verwendet werden. Das bedeutet allerdings, das wenn du $img_end[n]['avapic'] veränderst, auch gleichzeitig $img_repeat verändert wird - das solltest du beachten.

Kurz gesagt $img_end[n]['avapic'] und $img_repeat zeigen damit auf den gleichen Speicherbereich und haben damit den identischen Inhalt.
 
Zuletzt bearbeitet:
Zurück