Programação, Delphi, OutOfMemory, MemoryLeak
Conceito
Nesta página são disponibilizados alguns exemplos de vazamento de memória encontrados no projeto TotaliMiddlewareService.
ACBrNew
Recentemente alguns clientes relataram um aumento rápido no consumo de memória do Middleware.
Verificando o Gerenciador de Tarefas, o Middleware aumentava de tamanho a cada minuto.
Neste caso o objeto que causou o vazamento de memória foi o objeto TACBrNFSeX.
Podemos ver na imagem que há chamadas para outras funções após a instância da classe TACBrNFSeX.
O erro que ocorria neste caso era na função Self.ConfiguracaoTACBrNFSeXCfgEmpresa(Result, pCfgEmpresa) que lançava uma exceção.
Como no TTotaliACBrHelper.ACBrNFSeXNew não havia tratamento para exceções, a instancia da classe não era liberada da memória após ocorrer o erro.
Para corrigir colocamos todo o bloco de código após a instância da classe dentro de um Try..Except
Desta forma garantimos que caso ocorra alguma exceção, o objeto instanciado é liberado da memória antes de propagar a exceção adiante.
LoadGuarda
Durante os testes encontramos vários leaks com o objeto TGuarda.
Neste caso o objeto, que é declarado localmente, recebe a instancia do LoadGuarda:
Verificando no final da função, não é feita a liberação do objeto da memória.
Para corrigir basta liberar ao final da função. Este é o exemplo mais simples e fácil de encontrar.
Referência Perdida
Este exemplo na rotina PersisteParceiro, mostra um memory leak, que ocorreu por referência perdida (ou sobrescrição de objeto)
A classe TResultServicoPersisteParceiro tem uma property que também é um objeto. No create da classe é feito o também o create da property.

Na função fromJSON é feito o create da classe TResultServicoPersisteParceiro
Logo após podemos ver que a property Enderecos recebe uma nova instância vinda da função EnderecoFromJSON
Este create sobreescreveu o FEnderecos criado no Create da classe TResultServicoPersisteParceiro
Para corrigir podemos usar o Setter da própria property
Nele validamos se o objeto já está criado e liberamos da memória antes de setar um novo valor.
Também devemos ter certeza que o objeto está sendo liberado no destroy da classe.
Funções que retornam objetos
Encontrei vários casos onde o objeto não era destruído pois era o result de uma função.
Este result não era alocado em nenhum objeto e a instância era vazada imediatamente após executar a função.
Exemplo na TTUDocEmitidoDAO:

Esta função serve para retornar apenas um documento, porém a função ConsultaInterna retorna uma lista de documentos (TDocEmitido).
Para buscar o primeiro item da lista era feito um loop passando o índice diretamente para o Result, e após a primeira iteração o loop é quebrado (break).
Porém a lista não é liberada em lugar nenhum.
Esta implementação apresenta alguns problemas:
- Não é validado se a lista contém documentos.
- Não é desvinculado o objeto da lista. Mesmo se liberarmos a lista da memória, o objeto Result ficará inválido.
- Ocorre vazamento de memória, pois a lista não foi liberada
Como resolver neste caso.
Criamos uma variável local para a lista que retorna da função:
Dentro de um bloco try..finally, validamos o count lista, para evitar erros de "Invalid Pointer Operation".
Em seguida, extraímos o objeto, desvinculando-o da lista. Após isso podemos passá-lo para Result.
Após extraírmos podemos liberar a lista normalmente, não vai mais liberar o objeto.
Lembrar de destruir onde recebe o result da função CarregaDocumentoEmitido também!
Classes com erro de declaração
Um outro exemplo que encontramos foi na classe TVolume da unit TTUFrete
Nesta classe temos um objeto TEspecie que estava gerando leak.
Olhando o Destroy e o SetEspecie, tudo aparenta normal, porém o leak continuava.

O erro estava na declaração do método Destroy da classe Tvolume.
Estava declarado como OVERLOAD.
Ao chamar o Free do TVolume não era executado o destroy.
Para corrigir, basta declarar o destroy corretamente como OVERRIDE.
Isso serve para sobreescrever a função da classe “pai” com a nova implementação.
Ainda irá chamar o destroy da classe pai com INHERITED
JSONObject e JSONArray
Um exemplo que encontrei no TTUCommerceAPI.
Neste loop a ideia era popular o oJSONArray com o JSON convertido do objeto TChaveDocEmitido.
E na maioria das vezes o JSONArray se encarrega de destruir os objetos TJSONObject. Porém neste caso é adicionado somente o Texto em formato JSON.
O create da classe TChaveDocEmitido é feito no TChaveDocEmitido.fromDocEmitido, e é adicionado o array apenas o result da função TChaveDocEmitido.ToJSON.
A referência do objeto que executou o ToJSON não é passada para ninguém e perdida.
Como corrigir neste caso:
Criar o objeto separadamente, passando sua referência para uma variável.
Executar o que for preciso e liberar em seguida:
Páginas Relacionadas: