Das Memory Limit von PHP – wie funktioniert es?

Das Memory Limit, sprich die Speichergrenze, von PHP ist für viele oft ein Rätsel. Es ist nicht klar, wie bzw. wo es gesetzt wird und welche Auswirkungen die unterschiedlichen Einstellungsorte haben. Viele Anleitungen sind oft unvollständig, und tragen damit eher zur Irritation bei. Denn tatsächlich gibt es unzählige Möglichkeiten das Memory Limit zu setzen:

  • super-global in der php.ini
  • global in der pool.conf, bei Verwendung von PHP-FPM
  • oder global in der httpd.conf (Apache) oder server.conf (nginx)
  • auf Ordner-Ebene in der user.ini
  • auf Ordner-Ebene in der .htaccess-Datei
  • auf Applikations-Ebene über die WordPress-Konstanten WP_MEMORY_LIMIT und WP_MAX_MEMORY_LIMIT
  • oder auf Datei-Ebene im Script mit ini_set();
Unterschiedliche Orte, um das PHP Memory Limit (und andere Parameter) zu setzen

Die Reihenfolge entspricht übrigens der Hierarchie. Wird das Limit also im PHP-Script mit ini_set() gesetzt, überschreibt das die Einstellung in der httpd.conf oder server.conf. Wie sich das vermeiden lässt, dazu unten mehr. Das trifft natürlich nicht nur auf das Memory Limit, sondern so ziemlich jede PHP-Einstellung zu.

Außerdem hängen die Möglichkeiten stark davon ab, wie du PHP nutzt, also ob als Modul oder über CGI. Um das Vorgehen besser zu verdeutlichen, gehe ich im Folgenden von zwei WordPress-Setups aus. Einmal wird nginx als HTTP-Server genutzt und dort PHP-FPM über CGI angesprochen. Das zweite Setup nutzt Apache2 als HTTP-Server und dort PHP als Modul (php-mod).

Was bedeutet das Memory Limit von PHP?

Jeder Dienst benötigt für seine Ausführung Platz im Arbeitsspeicher. So auch PHP. Und mit jedem Aufruf eines PHP-Scripts wird PHP aktiviert und fordert somit Platz im Arbeitsspeicher ein. Und genau dort spielt die Speichergrenze eine Rolle: Um zu vermeiden, dass bei der Verarbeitung eines PHP-Scripts zu viel Speicher verbraucht und damit der ganze Server in Mitleidenschaft gezogen wird, legt man eine Obergrenze fest, das memory_limit.

Diese Grenze gilt für jeden einzelnen Script-Aufruf. Ein Beispiel: Dein Server hat 8 GByte (8.192 MByte) Arbeitsspeicher. Der Aufruf der Datei index.php, die eine sehr aufwendige Datenbankabfrage durchführt und damit viele Daten verarbeitet, erfordert 10 MByte Arbeitsspeicher (das ist übrigens relativ viel und entspricht etwa 5.000 bedruckten A4 Seiten). Das Memory-Limit ist auf 32 MByte eingestellt. Das physikalische Limit beträgt allerdings 8.192 MByte. Die index.php kann also 819 mal gleichzeitig (!) aufgerufen werden; erst dann ist der Arbeitsspeicher voll. Das Memory-Limit hat hier also keine Auswirkungen.

Gehen wir nun davon aus, dass bei einem der 819 Aufrufe bestimmte Parameter gesetzt werden, die eine weitaus größere Datenmenge in der Datenbank abfragen. Die Datenmenge erhöht sich auf 40 MByte. An dieser Stelle wird das memory_limit aktiv. Der Aufruf dieses Scripts wird abgebrochen mit der Fehlermeldung, dass die erlaubte Speichergrenze überschritten wurde. PHP wird in diesem Fall eine Fehlermeldung wie diese anzeigen:

Das Gute daran: Der Aufruf der anderen Scripte wird davon nicht in Mitleidenschaft gezogen.

Es gibt sehr viele Wege, das Speicherlimit zu setzen. Das hat Vorteile aber auch Nachteile: So wird die Suche nach den gesetzen Einstellungen oft zur Suche nach der berühmten Nadel im Heuhaufen. Der Vorteil: Du kannst das Speicherlimit sehr granular an die Anforderungen deiner verschiedenen Anwendungen anpassen. Bereit? Los gehts…

Grundsätzliches

Der Wert, den du für das Memory Limit übermittelst, kann als einfache Ziffer übergeben werden und legt die Speichergrenze somit in Byte fest. PHP unterstützt darüber hinaus einige Kurzschreibweisen, also z.B. K für Kilobyte (32K), M für Megabyte (256M) und G für Gigabye (2G).

Willst du verhindern möchtest, dass die Speichergrenze später an anderer Stelle überschrieben wird, definierst du sie mithilfe von php_admin_value anstatt nur php_value. Also z.B.:

Willst du das Memory LImit komplett deaktivieren, dann setzt du es auf -1:

Aber Achtung: Zu Testzwecken mag das sinnvoll sein, in einer Live-Umgebung solltest du immer mit einer Obergrenze arbeiten.

Und abschließend noch der Verweis auf zwei nützliche Funktionen, die eigentlich bekannt sein sollten:

Wichtig: Derartige Funktionen dienen der Fehlersuche und sollten im Live-System nicht unbedingt öffentlich abrufbar sein!

Das Memory Limit in PHP einstellen

Super global in der php.ini

Die erste “Konfigurations-Stufe” befindet sich natürlich direkt innerhalb von PHP. Wenn du wissen willst, wo sich die Einstellungen für PHP befinden, nutze ganz einfach phpinfo(); Die entsprechende Datei heißt php.ini und befindet sich in der Regel in /etc/php/7.0/apache2/php.ini (für PHP als Modul, z.B. in Apache2) oder /etc/php/7.4/fpm/php.ini (für PHP als Dienst, z.B. in nginx). Der Parameter ist in beiden Fällen gleich:

Wenn du PHP als Modul unter Apache nutzt, musst du Apache2 neustarten, bzw. die Konfiguration neu einlesen lassen:

Analog, bei PHP als Dienst, wie z.B. in nginx, erfolgt das Neustarten folgendermaßen (In der Regel reicht es aus, mit reload nur die Konfiguration neu zu laden. Sollte es zu Komplikationen kommen, hilft ein kompletter Neustart des Dienstes eventuell weiter).

Für jeden PHP-FPM Pool in der pool.conf

Nutzt du PHP-FPM über CGI (also mein erstes Setup mit nginx), gibt es für jeden Server (bzw. virtuellen Host) einen eigenen Pool, lies eine Art eigene getrennte PHP-Umgebung für jeden einzelnen virtuellen Host. Die Einstellungen dazu befinden sich z.B. unter /etc/php/7.4/fpm/pool/my_host.conf. Hier wird der entsprechende Parameter folgendermaßen gesetzt:

Die Einstellung an dieser Stelle überschreibt den Wert in der php.ini. Nach der Änderung muss der PHP-Dienst natürlich neu gestartet werden.

Das Memory-Limit in Apache2 und nginx setzen

Super global in der nginx.conf / httpd.conf

Für die beiden HTTP-Server nginx und Apache2 gibt es die beiden globalen Einstellungsdateien /etc/nginx/nginx.conf bzw. /etc/apache2/httpd.conf. In nginx kann das PHP-Limit im sogenannten http-Kontext gesetzt werden und gilt damit für die komplette Nginx-Instanz. Ich gehe hier und im Folgenden übrigens davon aus, dass nginx den PHP-Interpreter über FastCGI anspricht und PHP eigenständig als FPM läuft.

Im Falle von Apache2 gehe ich davon aus, dass PHP als Modul konfiguriert ist. Hier wird das PHP Limit in der httpd.conf folgendermaßen gesetzt:

Global für jeden virtuellen Host

In nginx lässt sich die entsprechende Einstellung auch innerhalb des Server-Blocks setzen und gilt somit für einen ganzen virtuelle Host. Üblicherweise findest du diese Einstellung in /etc/nginx/conf.d/my_host.conf:

Nutzt du Apache2, findest du die entsprechende Einstellungs-Datei in der Regel unter /etc/apache2/sites-enabled/my_host.conf:

Global für jeden Ordner

Weiter geht es mit dem Ordner-Spezifische Memory-Limit in nginx im location-Kontext (die Datei dürfte in der Regel unverändert sein).

Achtung: In nginx kann die Direktive fastcgi_param auch im übergeordneten http-Kontext oder auf Ordner-Ebene innerhalb des location-Blocks gesetzt werden:

Analog dazu kannst du in Apache2 das Limit global in apache2.conf oder auf Ordner-Ebene in der my_host.conf:

Das Memory-Limit auf Ordner-Ebene setzen

Für jeden Ordner mit der .user.ini

Mit der Datei .super.ini kannst du PHP-Einstellungen für beliebige Ordner individuell konfigurieren. Dieser Weg steht dir allerdings nur offen, wenn du PHP nicht als Modul nutzt, sondern über CGI/FastCGI. In meinem Beispiel-Setup mit Apache2 und php-mod fällt diese Option also weg.

Du kannst die Datei in jedem Ordner anlegen und dort Parameter so einstellen, wie du es von der php.ini gewohnt bist. Dieser Weg muss allerdings vorher “freigeschaltet” werden, indem in der php.ini (bzw. den anderen o.g. Orten), der folgende Parameter gesetzt ist:

Ob das der Fall ist, erfährst du, wie immer, mithilfe von phpinfo();. Dort siehst du unter “Additional .ini files parsed” auch, ob sich im aktuellen Ordner eine .user.ini befindet und diese gelesen wurde:

Überprüfen, welche .ini-Dateien PHP gelesen hat

Wenn das alles passt, verwendest du die .user.ini wie die php.ini und setzt das Speicherlimit demnach folgenermaßen:

Für jeden Ordner mit der .htaccess

Diese Option wiederum steht dir nur mit Apache2 zur Verfügung, da nginx keine .htaccess-Dateien unterstützt. Du setzt das Limit in der .htaccess-Datei folgendermaßen:

Das Memory-Limit auf Applikations-Ebene setzen

WordPress verwendet standardmäßig eine Memory Limit von 40 MByte im Frontend (bzw. 64 MByte bei einem Multi-Site-Setup) sowie (mindestens) 256 MByte für das Backend. Wenn du diese Werte ändern möchtest, kannst du dafür die beiden folgenden Konstanten in der wp-config.php setzen – achte darauf, diese Konstanten vor der Einbindung von wp-settings.php zu definieren:

WordPress nutzt intern allerdings auch nur die PHP-Funktion ini_set um das Memory Limit festzulegen:

Das Memory-Limit auf Datei-Ebene setzen

Zum Abschluss gibt es auch die Möglichkeiten das Memory Limit in jedem Script individuell zu setzen. Diese Möglichkeit steht an letzter Stelle und überschreibt damit alle vorhergehenden Einstellungen:

Durch die Verwendung von include(), require_once() oder require() lassen sich andere PHP-Dateien innnerhalb eines Scripts einzubinden. Grundsätzlich wird das Memory Limit dann zwar vererbt. Was nicht heißt, dass es durch später eingebundene Scripte überschrieben werden kann. Das macht sich z.B. auch bei WordPress bemerkbar: Du kannst das Memory Limit zwar mit WP_MEMORY_LIMIT für WordPress definieren, ein Plugin oder Theme kann das Limit aber trotzdem später überschreiben.

Ein Beispiel: So sieht die Datei index.php aus:

Die Datei file.php sieht folgendermaßen aus:

Die zweite Zeile in der file.php gibt als Rückgabe 256M aus. Das Memory-Limit wird vererbt. Die vierte Zeile setzt das Limit allerdings auf 32 MByte und überschreibt damit den Wert von 256 MByte. Belegt die “aufwendige Operation” in index.php nur 12 MByte, in der file.php nur jeweils 5 MByte, funktioniert das gesamte Script einwandfrei. Wenn die “aufwendige Operation” in der Datei index.php z.B. mehr als 22 MByte beansprucht, bricht PHP die Verarbeitung des Scripts ab. Warum? Weil das Limit nachträglich nach unten korrigiert wurde.

Schreibe einen Kommentar