O documento discute gerenciamento de memória no .NET, incluindo como o coletor de lixo funciona, as gerações de objetos no heap, boxing e unboxing, uso de Dispose() e SuppressFinalize(), variáveis estáticas, StringBuilder, e Span.
1. Gerenciamento de Memória
Carlos dos Santos
CDS Software
Microsoft MVP
Microsoft Regional Director
carlos@cds-software.com.br
www.cds-software.com.br
@cdssoftware
2. • Mais de 25 anos de experiência no desenvolvimento de software
• Responsável por Pesquisa e Desenvolvimento na CDS Informática
• Microsoft MVP desde 2008
• Microsoft Regional Director
• Um dos criadores do projeto open source CodeCracker: www.github.com/code-cracker
• Mantenedor do projeto open source BoletoNet: www.github.com/boletonet
• Blog: www.carloscds.net
• GitHub: www.github.com/carloscds
15. www.gotaskfly.com
• Cada processo tem seu próprio espaço de endereço virtual separado. Todos os processos no mesmo computador
compartilham a mesma memória física e arquivo de paginação, se houver algum.
• Como desenvolvedor de aplicativos, você trabalha apenas com o espaço de endereço virtual e nunca manipula a
memória física diretamente. O coletor de lixo aloca e libera memória virtual para você no heap gerenciado.
• Condições para coleta de lixo (GC):
• O sistema tem pouca memória física. Isso é detectado pela notificação de falta de memória do sistema operacional.
• A memória usada por objetos alocados no heap gerenciado ultrapassa o limite aceitável. Esse limite é ajustado
continuamente enquanto o processo é executado.
• O método GC.Collect() é chamado. Em quase todos os casos, você não precisa chamar esse método porque o coletor de
lixo funciona continuamente. Esse método é usado principalmente para situações exclusivas e testes.
• Chamar o GC.Collect() não indica que a memória será coletada naquele momento
16. www.gotaskfly.com
• O heap está organizado em gerações de modo que possa manipular objetos de vida útil longa e curta. A coleta
de lixo ocorre principalmente com a recuperação de objetos de vida útil curta, que geralmente ocupam apenas
uma pequena parte do heap. Há três gerações de objetos no heap:
• Geração 0. Essa é a geração mais jovem e contém objetos de vida útil curta. Um exemplo de um objeto de
vida útil curta é uma variável temporária. A coleta de lixo ocorre com mais frequência nessa geração.
• Geração 1. Essa geração contém objetos de vida útil curta e serve como um buffer entre objetos de vida útil
curta e longa. Normalmente passou pelo GC e por ter espaço no heap permaneceu.
• Geração 2. Essa geração contém objetos de vida útil longa. Um exemplo de um objeto de vida útil longa é
um objeto em um aplicativo para servidores que contém dados estáticos que estão vivos durante o
processo.
• Dicas:
• Use sempre Dispose(), trabalhe com using {}
• Não fique chamando o GC.Collect(), ele sabe quando precisa rodar!!!
• Cuidado com objetos não gerenciados: PInvoke
17. www.gotaskfly.com
• Vamos imaginar que temos 5 objetos criados – Gen 0:
• Agora o GC inicia e libera os objetos 1, 3 e 5 e move 2 e 4 para o início, enquanto criamos 6, 7, 8, 9 e 10. Ficamos
então com 2 e 4 na Gen 1 e o restante em Gen 0:
• Novamente o GC inicia, coletando alguns objetos, enquanto criamos novos:
• Imaginamos que a Gen 1 está cheia:
• Então os objetos “sobreviventes” da Gen 1 vão para Gen 2, e temos memória livre:
1 2 3 4 5
2 4 6 7 8 9 10
2 4 6 7 10 11 12 13 14 15
2 6 10 13 15 16 17 18 19 20
2 6 10 13 17 18 20
Gen 0 Gen 1 Gen 2
18. www.gotaskfly.com
• Boxing é o processo de conversão de ValueType para o tipo object, e Unboxing é o inverso.
• Veja no exemplo abaixo, a variável valor (inteiro) é boxed para obj (object):
• O objeto pode ser então unboxed para int:
Stack Heap
valor = 0
obj = valor; int: 0
int valor = 0;
object obj = valor;
obj = 100;
valor = (int)obj;
19. www.gotaskfly.com
• Ao chamar uma classe que implementa a interface IDisposable, teremos o método Dispose(), que é chamado
pelo GC.
• Utilizamos o Dispose() para liberar recursos não mais utilizados, principalmente não gerenciados.
• Utilizar o método using {} automaticamente chama o Dispose()
• GC.SuppressFinalize() indica para o GC que o objeto já foi “limpo” e não precisa ser coletado.
20. www.gotaskfly.com
• Variáveis estáticas não são coletadas pelo GC.
• Ela existem durante toda a execução da aplicação.
• Potencialmente perigosas em aplicações web, pois podem compartilhar dados entre as sessões.
22. www.gotaskfly.com
• System.Span<T> é um novo tipo de valor no .NET.
• Permite a representação de regiões contíguas de memória!
• Não importa se a memória está associada a um objeto gerenciado, foi fornecida por código nativo por interop
ou está na pilha.
• E ele faz isso oferecendo acesso seguro com características de desempenho semelhantes às das matrizes.
24. www.gotaskfly.com
• Degradação de performance ao longo do tempo – memory leak
• Problemas com escalabilidade, mais usuários, mais problemas (obs: você faz teste de carga?)
• Memoria não melhora mesmo reiniciando a aplicação – objetos temporários
• Como verificar este tipo de problema:
• Visual Studio Profiler
• Perfmon do Windows
• WinDbg