Sobre vazamento de memória

O Webman é um framework residente na memória, portanto, precisamos prestar atenção a situações de vazamento de memória. No entanto, os desenvolvedores não precisam se preocupar excessivamente, pois o vazamento de memória ocorre em condições extremamente raras e pode ser facilmente evitado. A experiência de desenvolvimento do Webman é basicamente a mesma que em frameworks tradicionais, não sendo necessário realizar operações adicionais de gerenciamento de memória.

Dica
O processo monitor que o Webman fornece monitora o uso de memória em todos os processos. Se o uso de memória de um processo estiver prestes a atingir o valor definido em memory_limit no php.ini, o processo correspondente será automaticamente reiniciado de forma segura, liberando memória, sem afetar os serviços durante esse período.

Definição de vazamento de memória

Com o aumento contínuo das requisições, a memória utilizada pelo Webman também cresce indefinidamente (note que é indefinidamente), podendo atingir centenas de megabytes ou mais, o que caracteriza um vazamento de memória. Se a memória aumentar, mas não continuar crescendo, isso não é considerado um vazamento de memória.

Em geral, o uso de dezenas de megabytes de memória por um processo é uma situação normal. Quando o processo lida com requisições muito grandes ou mantém uma quantidade massiva de conexões, o uso de memória de um único processo pode facilmente ultrapassar a marca de cem megabytes. Após o uso, o PHP pode não devolver toda essa memória ao sistema operacional, mas reservar para reutilização. Portanto, após processar uma determinada requisição grande, pode ocorrer uma situação onde o uso de memória aumenta, mas não é liberado, isso é um fenômeno normal. (O método gc_mem_caches() pode ser chamado para liberar parte da memória ociosa)

Como ocorre o vazamento de memória

Para que um vazamento de memória ocorra, devem ser atendidas ambas as seguintes condições:

  1. Existe um array com longo ciclo de vida (note que é um array com longo ciclo de vida, arrays comuns não são problemáticos)
  2. E esse array com longo ciclo de vida se expande indefinidamente (dados são constantemente inseridos nele pelo negócio, sem limpeza)

Se as condições 1 e 2 forem satisfeitas ao mesmo tempo (note que é ao mesmo tempo), então ocorrerá um vazamento de memória. Caso contrário, se não atender a essas condições ou apenas uma delas for satisfeita, não será considerado um vazamento de memória.

Arrays com longo ciclo de vida

No Webman, os arrays com longo ciclo de vida incluem:

  1. Arrays definidos com a palavra-chave static
  2. Propriedades de array em um singleton
  3. Arrays definidos com a palavra-chave global

Atenção
O Webman permite o uso de dados com longo ciclo de vida, mas é necessário garantir que os dados contidos não sejam indefinidos, ou seja, que o número de elementos não se expanda indefinidamente.

A seguir, exemplos para ilustrar

Array static em expansão indefinida

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

O array $data definido com a palavra-chave static é um array de longo ciclo de vida, e no exemplo, o array $data cresce indefinidamente conforme as requisições aumentam, levando a um vazamento de memória.

Propriedade de array em singleton em expansão indefinida

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

Código de chamada

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

Cache::instance() retorna um singleton Cache, que é uma instância de classe com longo ciclo de vida. Embora sua propriedade $data não use a palavra-chave static, a classe em si tem um longo ciclo de vida, então $data também é um array de longo ciclo de vida. Conforme diferentes chaves são adicionadas ao array $data, a memória utilizada pelo programa aumenta continuamente, causando um vazamento de memória.

Atenção
Se as chaves adicionadas em Cache::instance()->set(key, value) forem de quantidade limitada, não haverá vazamento de memória, pois o array $data não se expande indefinidamente.

Array global em expansão indefinida

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

Arrays definidos com a palavra-chave global não serão liberados após a execução da função ou método, portanto, são arrays de longo ciclo de vida. O código acima gerará um vazamento de memória conforme as requisições aumentem. Da mesma forma, arrays definidos com a palavra-chave static dentro de funções ou métodos também são arrays de longo ciclo de vida. Se o array se expandir indefinidamente, também causará vazamento de memória, por exemplo:

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

Recomendações

Recomendamos aos desenvolvedores que não se preocupem excessivamente com vazamentos de memória, pois são muito raros. Se, por acaso, ocorrer, podemos usar testes de carga para encontrar qual parte do código está causando o vazamento. Mesmo que os desenvolvedores não consigam encontrar o ponto de vazamento, o serviço monitor embutido no Webman reiniciará de forma segura os processos com vazamento de memória, liberando memória.

Se você realmente quiser evitar vazamentos de memória, considere as seguintes recomendações.

  1. Evite usar arrays definidos com as palavras-chave global e static; se precisar usá-los, garanta que eles não se expandam indefinidamente.
  2. Para classes que você não domina, evite usar singleton; inicialize com a palavra-chave new. Se precisar de um singleton, verifique se há propriedades de arrays em expansão indefinida.