Wallace ReisPublicado em 01/01/2010
Um vazamento de memória ocorre quando um programa aloca memória e não é capaz de liberar esta de volta ao sistema operational, ocasionando diminuição de desempenho da aplicação (ou até mesmo do sistema computacional como um todo). Muitas pessoas acreditam que qualquer crescimento inesperado do uso de memória seja um sintoma de vazamento, o que não é sempre uma verdade.
Perl usa contagem de referências
para gerênciar o uso de memória, isto significa que quando se cria uma estrutura
circular causa-se vazamento, como também um XS
prendendo uma referência. Deste modo, não é tão difícil saber quando e em
qual ordem a desalocação vai acontecer, e então pode-se ser pró-ativo
(ou paranóico :) chamando undef($object)
da mesma forma que os programadores
em C, free(object)
.
Vazamentos de memória podem não ser fácilmente detectáveis ou nem mesmo sérios em alguns casos, como por exemplo em scripts simples e/ou de curta duração. Em outros casos, pode-se fazer uso de alguns dos vários módulos existentes no CPAN como:
Investiga o uso de memória de variáveis.
Útil para encontrar referências circulares se você sabe ou suspeita quais estruturas são prováveis de se ter ciclos.
Indica onde estão as variáveis com vazamento.
Fornecem várias funções para monitoramento de vazamentos de memória.
Permite o monitoramento de objetos ao framework Devel::Events, facilitando a construção de ferramentas de relatório de ciclos.
Pode ser usado para encontrar ciclos em objetos existentes em códigos que fazem programação XS.
Valgrind é um framework para construção de ferramentas de análise dinâmica que contém vários utilitários prontos para detecção de falhas no gerenciamento de memória e threading. Muito útil se há suspeita de vazamento de memória em código XS.
Ciclos são contornáveis na prática. Porém, por vezes faz-se necessário
o uso de referências de retorno como uma simplicação na modelagem de
relacionamentos, neste caso, pode-se usar a função weaken
do Scalar::Util
para assim marcar uma referência fraca permitindo ao gerenciador coletar o
objeto como lixo e liberar a memória.
Exemplo:
1. my $parent = { name => 'Foo' };
2. my $child = {
name => 'Foo Jr',
parent => $parent,
};
3. $parent->{'children'} = [$child];
Usando a função find_cycle
do Devel::Cycle
4. find_cycle($parent);
teríamos como saída
Cycle (1):
$A->{'children'} => \@B
$B->[0] => \%C
$C->{'parent'} => \%A
onde $A
é $parent
e $C
é $child. Assim, obtém-se o ponto chave da
circularidade, o qual neste exemplo fica óbvio desde o ínicio onde seria,
pois é sua modelagem é bem simples e a profundidade dos relacionamentos
é pouca, facilitando ao desenvolvedor a correção.
1. use Scalar::Util 'weaken';
2. my $parent = { name => 'Foo' };
3. weaken($parent);
4. my $child = {
name => 'Foo Jr',
parent => $parent,
};
5. $parent->{'children'} = [$child];
Com Moose, basta declarar o atributo como weak_ref => 1
.
package Child;
use Moose;
has parent => (
isa => 'Parent', is => 'rw',
weak_ref => 1, required => 1,
);
1;
Se você tem uma aplicação web desenvolvida em Catalyst, existem dois componentes que permitem geração de relatórios sobre vazamentos de forma rápida e indolor.
Reliza o monitoramento de objetos que causaram vazamentos no durante um ciclo de requisição da aplicação.
Gera relatórios sobre os vazamentos de memória encontrados pelo Catalyst::Plugin::LeakTracker.
Tudo o que se precisa fazer é carregar o plugin na classe da aplicação
package MyApp;
use Catalyst qw(LeakTracker);
e criar um controller (por exemplo Leaks
)
package MyApp::Controller::Leaks;
use Moose;
BEGIN { extends 'Catalyst::Controller::LeakTracker' }
1;
desta forma, acessando http://localhost:3000/list_requests obtém-se um relatório semelhante a este:
Os resultados demonstrados são por requisição, incluem as ações/URIs que tiveram vazamento e quanto de memória cada uma delas custou, etc. Acessando cada registro pode-se ver um relatório mais detalhado sobre quais classes de objetos estão envolvidas nos vazamentos:
refinando novamente, tem-se o stacktrace da origem e a lista de ciclos capturados:
Sabe-se que podem ocorrer falsos positivos nos resultados, pois objetos cacheados ou singletons são considerados tecnicamente como vazamentos, assim não há problema contanto que cada um não se repita a cada requisição. Este cenário é tipicamente encontrado em grandes aplicações em suas primeiras requisições.
Wallace Reis Wallace Reis <wreis@cpan.org>