Riguardo alle perdite di memoria

webman è un framework in memoria persistente, quindi dobbiamo prestare attenzione al problema delle perdite di memoria. Tuttavia, gli sviluppatori non devono preoccuparsi troppo, poiché le perdite di memoria si verificano solo in condizioni molto estreme e sono facilmente evitabili. L'esperienza di sviluppo con webman è fondamentalmente simile a quella dello sviluppo con framework tradizionali, e non è necessario eseguire operazioni superflue per la gestione della memoria.

Suggerimento
Il processo monitor fornito da webman monitora l'utilizzo della memoria di tutti i processi. Se l'utilizzo della memoria di un processo si avvicina al valore impostato da memory_limit in php.ini, il processo verrà automaticamente riavviato in modo sicuro, liberando la memoria senza alcun impatto sulle attività aziendali.

Definizione di perdita di memoria

Con l'aumento continuo delle richieste, la memoria occupata da webman aumenta infinito (nota: infinito), raggiungendo centinaia di MB o anche di più; questo è una perdita di memoria. Se la memoria cresce ma poi smette di crescere, non si considera una perdita di memoria.

È normale che un processo utilizzi decine di MB di memoria. Quando un processo gestisce richieste molto grandi o mantiene un gran numero di connessioni, l'occupazione di memoria di un singolo processo può anche superare i cento MB. Questa memoria utilizzata potrebbe non essere completamente restituita al sistema operativo da php. Viene invece mantenuta per il riutilizzo, quindi potrebbe verificarsi un aumento dell'occupazione della memoria dopo la gestione di una grande richiesta, senza rilasciare la memoria; questo è un fenomeno normale. (L'invocazione del metodo gc_mem_caches() può liberare parte della memoria non utilizzata.)

Come avviene la perdita di memoria

Una perdita di memoria si verifica solo se sono soddisfatti i seguenti due requisiti:

  1. Esiste un array a lungo ciclo di vita (nota: è un array a lungo ciclo di vita, non un normale array)
  2. E questo array a lungo ciclo di vita si espande all'infinito (i dati vengono continuamente inseriti nell'array senza mai pulirli)

Se entrambi i requisiti 1 e 2 sono soddisfatti contemporaneamente (nota: devono essere soddisfatti contemporaneamente), allora si verificherà una perdita di memoria. Se non si soddisfano i requisiti sopra o se si soddisfa solo uno di essi, non si tratta di una perdita di memoria.

Arrays a lungo ciclo di vita

In webman, gli arrays a lungo ciclo di vita comprendono:

  1. Arrays definiti con la parola chiave static
  2. Proprietà degli array in un singleton
  3. Arrays definiti con la parola chiave global

Nota
In webman è consentito utilizzare dati a lungo ciclo di vita, ma è necessario garantire che i dati contenuti in questi dati siano limitati e che il numero di elementi non si espanda all'infinito.

Di seguito vengono forniti esempi per illustrare.

Array statico in espansione infinita

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

L'array $data definito con la parola chiave static è un array a lungo ciclo di vita e, nell'esempio, l'array $data si espande continuamente man mano che le richieste aumentano, causando una perdita di memoria.

Proprietà di array singleton in espansione infinita

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

Codice di chiamata

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

Cache::instance() restituisce un singleton Cache, che è un'istanza di classe a lungo ciclo di vita. Anche se la proprietà $data non utilizza la parola chiave static, poiché la classe stessa è a lungo ciclo di vita, anche $data è un array a lungo ciclo di vita. Man mano che vengono aggiunti dati a $data con chiavi diverse, l'occupazione di memoria del programma aumenta, causando una perdita di memoria.

Nota
Se le chiavi aggiunte tramite Cache::instance()->set(key, value) sono di quantità limitata, non si verificherà una perdita di memoria, poiché l'array $data non si espande all'infinito.

Array global in espansione infinita

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

L'array definito con la parola chiave global non verrà deallocato dopo che una funzione o un metodo della classe è stato eseguito, quindi è un array a lungo ciclo di vita. Il codice sopra, con l'aumento delle richieste, causerà una perdita di memoria. Analogamente, un array definito con la parola chiave static all'interno di una funzione o di un metodo è anch'esso un array a lungo ciclo di vita e, se l'array si espande all'infinito, causerà una perdita di memoria, ad esempio:

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

Raccomandazioni

Si consiglia agli sviluppatori di non prestare particolare attenzione alle perdite di memoria, poiché esse si verificano raramente. Se si verifica una perdita di memoria, possiamo identificare il codice responsabile attraverso test di stress, per poi localizzare il problema. Anche se gli sviluppatori non riescono a trovare il punto di perdita, il servizio monitor di webman riavvierà in modo sicuro il processo che ha subito la perdita di memoria, liberando la memoria.

Se desideri davvero cercare di evitare le perdite di memoria, puoi seguire i seguenti suggerimenti.

  1. Cerca di non utilizzare arrays definiti con le parole chiave global o static, e se li utilizzi, assicurati che non possano espandersi all'infinito.
  2. Per classi non familiari, cerca di non utilizzare singleton, ma inizializza con la parola chiave new. Se è necessario un singleton, controlla se ci sono proprietà di array in espansione infinita.