À propos des fuites de mémoire

Webman est un framework en mémoire résidente, donc nous devons prêter un peu d'attention aux fuites de mémoire. Cependant, les développeurs ne doivent pas s'inquiéter outre mesure, car les fuites de mémoire se produisent dans des conditions très extrêmes et sont facilement évitables. L'expérience de développement avec Webman est essentiellement similaire à celle des frameworks traditionnels, il n'est pas nécessaire de faire des opérations superflues pour la gestion de la mémoire.

Remarque
Le processus de monitor intégré à Webman surveillera l'utilisation de la mémoire de tous les processus. Si l'utilisation de la mémoire d'un processus est sur le point d'atteindre la valeur fixée par memory_limit dans php.ini, il redémarrera automatiquement et en toute sécurité le processus concerné pour libérer de la mémoire, sans impact sur les activités commerciales.

Définition d'une fuite de mémoire

À mesure que le nombre de requêtes augmente, la mémoire utilisée par Webman augmente également de manière illimitée (notez qu'il s'agit d'une augmentation illimitée), atteignant plusieurs centaines de Mo, voire plus ; c'est ce qui constitue une fuite de mémoire. Si la mémoire augmente, mais ne continue pas à croître par la suite, cela ne compte pas comme une fuite de mémoire.

Une utilisation de plusieurs dizaines de Mo par processus est tout à fait normale. Lorsque le processus traite des requêtes très volumineuses ou maintient un grand nombre de connexions, l'utilisation de mémoire d'un seul processus peut atteindre plusieurs centaines de Mo. Après usage, PHP peut ne pas rendre toute cette mémoire au système d'exploitation. Au lieu de cela, il la conserve pour une réutilisation ultérieure, il est donc normal de constater que l'occupation de la mémoire augmente après le traitement d'une grande requête, sans que la mémoire ne soit libérée (l'appel de la méthode gc_mem_caches() peut libérer une partie de la mémoire inutilisée).

Comment se produisent les fuites de mémoire

Les fuites de mémoire se produisent si et seulement si les deux conditions suivantes sont remplies :

  1. Il existe un tableau à longue durée de vie (notez qu'il s'agit d'un tableau à longue durée de vie, les tableaux ordinaires ne posent pas de problème).
  2. Et ce tableau à longue durée de vie s'étend indéfiniment (les données sont insérées dans celui-ci sans jamais être nettoyées).

Si les conditions 1 et 2 sont toutes deux satisfaites (notez qu'elles doivent être toutes les deux satisfaites), alors une fuite de mémoire se produira. Inversement, ne pas respecter ces conditions ou n'en respecter qu'une seule ne constitue pas une fuite de mémoire.

Tableaux à longue durée de vie

Les tableaux à longue durée de vie dans Webman comprennent :

  1. Les tableaux définis avec le mot clé static.
  2. Les propriétés de tableau d'un singleton.
  3. Les tableaux définis avec le mot clé global.

Attention
Webman autorise l'utilisation de données à longue durée de vie, mais il est nécessaire de s'assurer que les données contenues dans ces tableaux sont limitées, et que le nombre d'éléments ne s'étendra pas indéfiniment.

Voici quelques exemples pour illustrer.

Tableau static en expansion infinie

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

Le tableau $data défini avec le mot clé static est un tableau à longue durée de vie, et dans cet exemple, le tableau $data s'expand à chaque requête, entraînant une fuite de mémoire.

Propriété de tableau de singleton en expansion infinie

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

Code d'appel

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

Cache::instance() retourne une instance unique de Cache, qui est une instance de classe à longue durée de vie. Bien que sa propriété $data ne soit pas définie avec le mot clé static, la classe elle-même étant à longue durée de vie, $data est également un tableau à longue durée de vie. À mesure que différents clés sont ajoutées au tableau $data, la mémoire utilisée par le programme augmente également, entraînant une fuite de mémoire.

Attention
Si les clés ajoutées par Cache::instance()->set(key, value) sont d'un nombre limité, alors il n'y aura pas de fuite de mémoire, car le tableau $data ne s'étend pas indéfiniment.

Tableau global en expansion infinie

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

Un tableau défini avec le mot clé global ne sera pas récupéré après l'exécution de la fonction ou de la méthode de classe, il constitue donc un tableau à longue durée de vie. Le code ci-dessus, à mesure que le nombre de requêtes augmente, entraînera une fuite de mémoire. De même, un tableau défini avec le mot clé static dans une fonction ou une méthode sera également un tableau à longue durée de vie, et s'il s'étend indéfiniment, une fuite de mémoire se produira également, par exemple :

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

Recommandations

Il est conseillé aux développeurs de ne pas s'inquiéter particulièrement des fuites de mémoire, car elles se produisent très rarement. Si, par malheur, cela se produit, nous pouvons identifier le segment de code qui cause la fuite grâce aux tests de pression, permettant ainsi de localiser le problème. Même si le développeur ne trouve pas le point de fuite, le service monitor intégré à Webman redémarrera automatiquement et en toute sécurité les processus concernés pour libérer de la mémoire.

Si vous souhaitez vraiment essayer de minimiser les fuites de mémoire, vous pouvez suivre ces recommandations :

  1. Évitez autant que possible les tableaux définis avec les mots clés global et static, et s'ils sont utilisés, assurez-vous qu'ils ne s'étendent pas indéfiniment.
  2. Pour les classes que vous ne connaissez pas bien, essayez d'éviter d'utiliser des singletons ; utilisez plutôt le mot clé new pour les initialiser. Si un singleton est nécessaire, vérifiez s'il a des propriétés de tableau en expansion infinie.