C# - Garbage Collection, Dipose e Finalize
Neste artigo vamos tratar do garbage collector e mostrar como funcionam os métodos Dispose e Finalize usados para liberar o espaço alocado pelos objetos. |
O coletor de lixo (garbage Collector) do .NET Framework gerencia a alocação e liberação de memória para sua aplicação. Cada vez que você cria um novo objeto, o Common Language Runtime aloca memória para o objeto a partir do heap gerenciado. Enquanto os espaços de endereços estão disponíveis no heap gerenciado, o runtime continua a alocar espaço para novos objetos.
Nota: Quando você iniciar um novo processo, o runtime reserva uma região contígua de espaço de endereço para o processo. Este espaço de endereço reservado é chamado de heap gerenciado.
No entanto, a memória não é infinita. Eventualmente, o coletor de lixo deve executar uma coleta, a fim de liberar memória. O motor de otimização do coletor de lixo determina o melhor momento para realizar a coleta, com base nas alocações feitas.
Quando o coletor de lixo executa uma coleta, ele verifica se há objetos no heap gerenciado que não estão mais sendo usados pelo aplicativo e executa as operações necessárias para recuperar sua memória.
Assim, a coleta
de lixo é um processo que libera automaticamente a memória de objetos que não
mais estão em uso. A decisão de recorrer ao processo de destruição é feita por
um programa especial conhecido como coletor de lixo (Garbage Collector). No entanto, quando um
objeto perde o escopo no final do método Main(), o processo de destruição não é
necessariamente invocado.
Assim, você não pode determinar quando o método destruidor será chamado. O
coletor de lixo também identifica os objetos que não são mais referenciados no
programa e libera a memória alocada para eles. Você não pode destruir um objeto explicitamente no código. Na
verdade, isso é uma prerrogativa do coletor de lixo, que destrói os objetos para
os programadores. O processo de coleta de lixo acontece automaticamente. Ele
garante que:
· Objetos são destruídos: Ele não especifica quando o objeto será destruído.
· Apenas os objetos não utilizados são destruídos: um objeto nunca é destruído
se ele mantém a referência de um outro objeto.
A linguagem C# fornece métodos especiais que são usadospara liberar a instância de uma
classe a partir da memória, são eles : Finalize() e Dispose().
Finalize()
O método destruidor Finalize() é um método especial que é chamado a partir de uma classe para qual ele pertence ou a partir de classes derivadas. Ele é chamado depois da última referência de um objeto ser liberada da memória.
A plataforma .NET executa automaticamente o destruidor Finalize para destruir os objetos na memória. É importante lembrar porém que este método não pode ser executado imediatamente quando um objeto perde o escopo, pois o Commom Languagem Runtime (CLR) chama o destruidor Finalize() utilizando um sistema chamado de coleta de lixo de rastreamento de referência na qual a CLR verifica periodicamente os objetos que não estão sendo usados na aplicação. Quando um objeto é encontrado o método Finalize() é chamado e o coletor de lixo libera o objeto da memória.
Mas atenção, o método Finalize não pode ser chamado explicitamente no código. Só o coletor de lixo pode chamar o Finalize quando os objetos se tornam inacessíveis.
O método Finalize usa muitos recursos do sistema e não limpa a memória imediatamente, ele também não pode ser aplicado diretamente, só pode ser implementado via declaração do destruidor. A seguir temos um exemplo de como declarar o método. É recomendável implementar os métodos Finalize e Dispose em conjunto, se você precisar implementar o método Finalize. Após a compilação o destruidor se torna o método Finalize.
using System;
namespace Finalize_Dispose
{
public class MinhaClasse : IDisposable
{
//Construtor
public MinhaClasse()
{//inicialização
}
//Destruidor também chamado Finalize
~MinhaClasse()
{
this.Dispose();
}
public void Dispose()
{
//código para liberar os recursos não gerenciados
}
}
}
|
Então, quando a implementar
Finalize?
Pode ser durante a utilização de qualquer recurso não gerenciado como um Stream de arquivo declarado no
nível de classe. Podemos não estar sabendo em que fase ou etapa deve ser
apropriada para fechar o arquivo. Este objeto está sendo usado em muitos lugares
no aplicativo. Portanto, neste cenário o Finalize pode ser apropriado onde o
recurso não gerenciado pode ser liberado.
Dispose()
O método Dispose() é chamado para liberar recursos, como a conexão com um banco de dados, tão logo o objeto usando o recurso não esta mais sendo usado. Diferente do método Finalize() o método Dispose() não é chamado automaticamente e você precisa chamá-lo explicitamente a partir de uma aplicação cliente quando um objeto não mais for necessário. A interface IDisposable contém o método Dispose e por isso para chamar este método a classe precisa implementar esta interface.
Há alguns recursos que GC não é capaz de liberar uma vez que não dispõe de informações para reivindicar a memória desses recursos como manipuladores de arquivos, manipuladores de janelas, sockets de rede, conexões de banco de dados, etc
Por exemplo, se você abrir um arquivo no programa e não fechá-lo após o processamento, o arquivo não estará disponível para outras operações ou ele está sendo usado por outra aplicação que não podem abrir ou modificar o arquivo. Para este fim classe FileStream fornece o método Dispose. Temos de chamar esse método após o processamento do arquivo ser concluído, caso contrário, ocorrerá uma exceção de acesso negado ou de que o arquivo está sendo usado por outro programa.
Usando Finalize() ou Dispose()
Alguns objetos expõe os métodos Close e Dispose. Para as classes Stream ambos têm a mesma finalidade. O método Dispose() chama o método Close no seu interior.
void Dispose () { this.Close (); } |
Mas por que precisamos do método Dispose() em um Stream ?
Usando o método Dispose você poderá escrever o código abaixo e implicitamente chamar o método Dispose() e, finalmente, vai chamar o método Close.
using (FileStream arquivo = new FileStream ("caminho", FileMode.Open, FileAccess.READ)) { // Faça algo com o arquivo } |
Mas, para algumas classes ambos os métodos se comportam um pouco diferente; Por exemplo a classe Connection.
Se o método Close() for chamado ela irá
desconectar do banco de dados e liberar todos os recursos utilizados pelo
objeto de conexão e o método Open vai reconectá-lo novamente com o banco de dados
sem reinicializar o objeto de conexão.
Usando o método Dispose, no entanto, você libera completamente o objeto de conexão
que não pode ser reaberto apenas chamando o método Open
dessa forma o objeto de conexão terá que ser reiniciado.
Resumo comparativo:
Finalize | Dispose |
Usado para liberar recursos não gerenciados como arquivos, conexões de banco de dados, recursos COM, etc. retidos por um objeto antes que objeto seja destruído. | Ele é usado para liberar recursos não gerenciados como arquivos, conexões de banco de dados, etc COM a qualquer momento. |
Internamente é chamado pelo coletor de lixo e não pode ser chamado pelo código do usuário. | É chamado explicitamente pelo código do usuário, a classe que o define deve implementar a interface IDisposable. |
Pertence a classe Object | Pertence à interface IDisposable. |
Implementar quando você tem recursos não gerenciados em seu código, e quer ter certeza de que esses recursos são liberados quando a coleta de lixo acontecer. | Implementar quando você está escrevendo uma classe personalizada que será usada por outros usuários. |
Há custo de desempenho associado. | Não há custo de desempenho associado |
Lembretes Importantes:
Você não pode sobrescrever o método Finalize no C# ou C++. Mas você pode fazer isso no VB.NET uma vez que a linguagem não suporta um destruidor.
Você não deve implementar um método Finalize para objetos gerenciados, porque o coletor de lixo limpa recursos gerenciados automaticamente.
Um método Dispose deve chamar o método GC.SuppressFinalize() para o objeto de uma classe que tem destruidor porque ele já fez o trabalho para limpar o objeto, então não é necessário que o coletor de lixo chame o método Finalize do objeto.
E estamos conversados...
Todo o que o Pai me dá virá a mim; e o que vem a mim de maneira nenhuma o lançarei fora.João 6:37
João 6:38
Porque eu desci do céu, não para fazer a minha vontade, mas a vontade daquele que me enviou.João 6:39
E a vontade do que me enviou é esta: Que eu não perca nenhum de todos aqueles que me deu, mas que eu o ressuscite no último dia.
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? |
Gostou ?
Compartilhe no Facebook
Compartilhe no Twitter
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#