Über Speicherlecks

Webman ist ein im Speicher verankertes Framework, daher sollten wir ein wenig auf die Möglichkeit von Speicherlecks achten. Entwickler brauchen sich jedoch nicht übermäßig Sorgen zu machen, da Speicherlecks unter extremen Bedingungen auftreten und leicht vermieden werden können. Die Entwicklung mit Webman ähnelt der Erfahrung mit traditionellen Frameworks, sodass keine zusätzlichen Maßnahmen zur Speicherverwaltung getroffen werden müssen.

Hinweis
Der mitgelieferte Monitor-Prozess von Webman überwacht die Speichernutzung aller Prozesse. Wenn der Speicher eines Prozesses kurz davor steht, den in php.ini festgelegten Wert memory_limit zu erreichen, wird der betreffende Prozess automatisch sicher neu gestartet, um den Speicher freizugeben, ohne dass dies Auswirkungen auf den Geschäftsbetrieb hat.

Definition von Speicherlecks

Mit zunehmender Anzahl an Anfragen steigt der von Webman verwendete Speicher unbegrenzt (bitte beachten Sie, dass es unbegrenzt ist), bis hin zu mehreren hundert MB oder mehr, was ein Speicherleck darstellt. Wenn der Speicher zwar wächst, aber daraufhin nicht weiter wächst, zählt dies nicht als Speicherleck.

Ein normaler Speicherverbrauch von mehreren Dutzend MB pro Prozess ist ganz normal. Wenn ein Prozess große Anfragen bearbeitet oder eine große Anzahl von Verbindungen verwaltet, kann der Speicherverbrauch eines einzelnen Prozesses leicht über 100 MB steigen. In solchen Fällen gibt PHP möglicherweise nicht den gesamten Speicher an das Betriebssystem zurück, sondern hält ihn für die Wiederverwendung zurück, was dazu führen kann, dass der Speicherverbrauch nach der Bearbeitung einer großen Anfrage ansteigt und nicht freigegeben wird. Dies ist ein normales Verhalten. (Der Aufruf der Methode gc_mem_caches() kann einen Teil des freien Speichers freigeben.)

Wie Speicherlecks entstehen

Ein Speicherleck tritt nur dann auf, wenn folgende zwei Bedingungen erfüllt sind:

  1. Es gibt ein langlebiges Array (beachten Sie, es ist ein langlebiges Array, normale Arrays sind unproblematisch).
  2. Und dieses langlebige Array dehnt sich unbegrenzt aus (das Geschäftsmodell fügt unendlich viele Daten hinzu, ohne diese zu bereinigen).

Wenn die Bedingungen 1 und 2 gleichzeitig erfüllt sind (Achtung: gleichzeitig erfüllt), entsteht ein Speicherleck. Andernfalls, wenn eine oder keine der Bedingungen erfüllt ist, handelt es sich nicht um ein Speicherleck.

Langlebige Arrays

In Webman gibt es langlebige Arrays wie folgt:

  1. Arrays mit dem Schlüsselwort static
  2. Einzelinstanz-Array-Attribute
  3. Arrays mit dem Schlüsselwort global

Achtung
In Webman ist die Verwendung langlebiger Daten zulässig, es muss jedoch sichergestellt werden, dass die Daten innerhalb dieser Arrays endlich sind und die Anzahl der Elemente sich nicht unbegrenzt ausdehnt.

Im Folgenden werden entsprechende Beispiele gegeben.

Unbegrenzt wachsende static-Arrays

class Foo
{
    public static $data = [];
    public function index(Request $request)
    {
        self::$data[] = time();
        return response('hello');
    }
}

Das mit dem Schlüsselwort static definierte Array $data ist ein langlebiges Array, und im Beispiel wächst das Array $data mit jeder Anfrage kontinuierlich, was zu einem Speicherleck führt.

Unbegrenzt wachsende Einzelinstanz-Array-Attribute

class Cache
{
    protected static $instance;
    public $data = [];

    public function instance()
    {
        if (!self::$instance) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    public function set($key, $value)
    {
        $this->data[$key] = $value;
    }
}

Aufrufcode

class Foo
{
    public function index(Request $request)
    {
        Cache::instance()->set(time(), time());
        return response('hello');
    }
}

Cache::instance() gibt eine Cache-Einzelinstanz zurück, die eine langlebige Klasseninstanz ist. Obwohl das Attribut $data nicht das Schlüsselwort static verwendet, ist es aufgrund der langlebigen Klasse ebenfalls ein langlebiges Array. Wenn kontinuierlich verschiedene Schlüssel in das $data-Array hinzugefügt werden, wächst der Speicherverbrauch des Programms stetig und verursacht ein Speicherleck.

Achtung
Wenn die durch Cache::instance()->set(key, value) hinzugefügten Schlüssel eine begrenzte Anzahl haben, wird kein Speicherleck auftreten, da sich das $data-Array nicht unbegrenzt ausdehnt.

Unbegrenzt wachsende global-Arrays

class Index
{
    public function index(Request $request)
    {
        global $data;
        $data[] = time();
        return response($foo->sayHello());
    }
}

Das mit dem Schlüsselwort global definierte Array wird nach der Ausführung der Funktion oder Method nicht freigegeben, es handelt sich daher um ein langlebiges Array. Der obige Code verursacht mit zunehmenden Anfragen ein Speicherleck. Gleiches gilt für in Funktionen oder Methoden mit dem Schlüsselwort static definierte Arrays, die ebenfalls langlebig sind. Wenn diese Arrays unbegrenzt wachsen, kommt es ebenfalls zu Speicherlecks, wie im folgenden Beispiel:

class Index
{
    public function index(Request $request)
    {
        static $data = [];
        $data[] = time();
        return response($foo->sayHello());
    }
}

Empfehlungen

Es wird empfohlen, dass Entwickler sich nicht besonders um Speicherlecks kümmern, da sie sehr selten auftreten. Falls sie leider doch auftreten, können wir durch Lasttests herausfinden, welcher Teil des Codes das Leck verursacht und damit das Problem lokalisieren. Selbst wenn Entwickler den Ursprung des Lecks nicht finden, wird der von Webman mitgelieferte Monitor-Dienst gegebenenfalls sicher die Prozesse, in denen Speicherlecks auftreten, neu starten und den Speicher freigeben.

Wenn Sie wirklich versuchen möchten, Speicherlecks zu vermeiden, beachten Sie bitte die folgenden Empfehlungen:

  1. Verwenden Sie nach Möglichkeit keine Arrays mit den Schlüsselwörtern global oder static. Wenn Sie sie verwenden, stellen Sie sicher, dass sie sich nicht unbegrenzt ausdehnen.
  2. Verwenden Sie bei unbekannten Klassen nach Möglichkeit keine Einzelinstanzen, sondern initialisieren Sie sie mit dem Schlüsselwort new. Wenn eine Einzelinstanz erforderlich ist, überprüfen Sie, ob sie unbegrenzt wachsende Array-Attribute hat.