Importância do Gerenciamento de Memória Adequado
Por que o gerenciamento adequado de memória é tão importante para uma aplicação web?
A resposta é simples: como qualquer outra aplicação de servidor, os servidores uniGUI são projetados para rodar continuamente. Qualquer operação de memória prejudicial pode afetar adversamente a funcionalidade e a estabilidade do servidor. Esses efeitos podem permanecer ocultos e não detectados quando a carga do servidor é baixa, mas se tornarão visíveis à medida que mais clientes requisitarem serviços.
Agora vamos discutir essas operações de memória prejudiciais com mais detalhes.
Vazamento de memória
Um vazamento de memória acontece quando uma memória alocada manualmente nunca é liberada. As fontes de vazamentos de memória podem ser diversas. Um erro muito comum é criar um objeto dinamicamente sem adicionar código apropriado para descartá‑lo. O código a seguir mostra um exemplo de vazamento de memória:
A := TSomeControl.Create(nil);
A.Property1 := 0;
A.Run;
A.Free;À primeira vista o código acima pode parecer correto, já que chama o Free() método para descartar o controle criado. Contudo, o que acontecerá se o Run() método falhar e levantar uma exceção? Nesse caso a chamada a Free() será ignorada e ocorrerá um vazamento de memória. Esta é a versão corrigida do mesmo código:
A := TSomeControl.Create(nil);
try
A.Property1 := 0;
A.Run;
finally
A.Free;
end;No trecho anterior o Free() método será sempre chamado independentemente do código em try .. finally bloco.
O código seguinte mostra uma solução errada (não produzirá um vazamento de memória, mas causará corrupção de memória):
Se a primeira instrução falhar ao criar o objeto A lançando uma exceção, a solicitação subsequente para liberar A tentará liberar uma variável não inicializada. A falha em criar o objeto não atribui nil à variável. Tentar liberar um objeto apontando para uma localização aleatória pode criar corrupção de memória ou disparar uma Violação de Acesso.
Corrupção de memória
Uma corrupção de memória ocorre quando uma operação de memória sobrescreve outra localização de memória já ocupada por um objeto ou variável. Corrupções de memória são mais prejudiciais do que vazamentos de memória. Podem permanecer ocultas por algum tempo, mas eventualmente você começará a observar erros de Violação de Acesso na sua aplicação e nos arquivos de log do uniGUI.
Existem muitas fontes de corrupção de memória, a maioria resultante de maus hábitos de programação. Alguns hábitos podem não ser prejudiciais em uma aplicação VCL de desktop, mas em uma aplicação de servidor seus efeitos adversos se tornarão aparentes.
Talvez uma fonte primária de corrupção de memória em uma aplicação de servidor multithread seja o uso de variáveis globais. Se uma aplicação de servidor precisa de variáveis globais, é necessário proteger o acesso a elas. Em uma aplicação VCL padrão o uso de variáveis globais é comum, mas em uma aplicação multithread elas devem ser usadas somente quando estritamente necessário.
Enquanto o uso de variáveis ordinais como inteiros não levará a uma corrupção de memória imediata, o uso de strings globais dinâmicas pode causar corrupção de memória. A corrupção de memória é apenas um efeito colateral do uso de variáveis globais — outra questão importante é que globais são compartilhadas entre sessões.
Considere este cenário:
Você tem uma aplicação com um formulário de login e salva as informações do usuário em uma variável do tipo registro.
Agora declare‑o como uma variável global em uma unit (a maneira errada):
Após um novo usuário fazer login, atribua o nome do usuário a essa variável:
Existem dois grandes problemas com esse cenário:
Quando uma nova sessão é criada e o usuário #1 faz login, o nome dele é atribuído à
CurrentUservariável. Se outra sessão for criada e o usuário #2 fizer login,CurrentUseré sobrescrito com as novas informações do usuário para a sessão atual. A primeira sessão perde as informações do usuário previamente salvas.Esse cenário pode facilmente levar a corrupções de memória severas. Com tráfego mesmo moderado, escritas concorrentes de múltiplas threads podem causar corrupção de strings. Em Delphi, strings são locais de memória dinâmica gerenciados pelo gerenciador de memória. Quando duas threads concorrentes escrevem na mesma string, pode ocorrer corrupção de ponteiros.
Abordagem correta no uniGUI
No uniGUI, cada sessão vem com seu conjunto de módulos e objetos que são públicos para essa sessão mas privados para as demais. Formulários e módulos são exemplos desses objetos. Cada sessão tem sua própria cópia privada do MainForm, outros formulários, MainModule e DataModules (DataModules criados com o assistente do uniGUI).
Se você quer uma variável "global" para uma sessão, declare‑a como um campo ou propriedade de um desses objetos específicos da sessão. Fazendo isso, a variável é global ou pública para sua sessão pai, mas privada para outras sessões, evitando os problemas de memória descritos acima.
Reimplemente o cenário usando MainModule como a classe contêiner para UserRecord. Adicione uma nova propriedade nomeada UserRecord ao TUniMainModule classe:
Funções auxiliares e implementação:
Isso garante que cada sessão acesse sua própria cópia privada de UserRecord, evitando problemas de memória causados por variáveis globais.
Finalmente, acesse UserRecord a partir de outros formulários e módulos desta forma: