Atualização (1 de dezembro de 2009):
Gostaria de alterar esta resposta e admitir que a resposta original foi falha.
A análise original se aplica a objetos que requerem finalização - e o ponto em que as práticas não devem ser aceitas na superfície sem um entendimento preciso e profundo ainda permanece.
No entanto, verifica-se que DataSets, DataViews, DataTables suprimem a finalização em seus construtores - é por isso que chamar Dispose () neles explicitamente não faz nada.
Presumivelmente, isso acontece porque eles não têm recursos não gerenciados; portanto, apesar do MarshalByValueComponent conceder permissões para recursos não gerenciados, essas implementações específicas não têm a necessidade e podem, portanto, renunciar à finalização.
(Que os autores do .NET cuidem de suprimir a finalização nos tipos que normalmente ocupam mais memória fala da importância dessa prática em geral para os tipos finalizáveis.)
Não obstante, esses detalhes ainda estão sub-documentados desde o início do .NET Framework (quase 8 anos atrás) é bastante surpreendente (que você é essencialmente deixado por seus próprios dispositivos para peneirar material ambíguo e conflitante para juntar as peças às vezes é frustrante, mas fornece uma compreensão mais completa da estrutura em que confiamos todos os dias).
Depois de muita leitura, aqui está o meu entendimento:
Se um objeto requer finalização, ele pode ocupar memória mais do que o necessário - eis o porquê: a) Qualquer tipo que define um destruidor (ou herda de um tipo que define um destruidor) é considerado finalizável; b) Na alocação (antes da execução do construtor), um ponteiro é colocado na fila de finalização; c) Um objeto finalizável normalmente exige que 2 coleções sejam recuperadas (em vez do padrão 1); d) Suprimir a finalização não remove um objeto da fila de finalização (conforme relatado por! FinalizeQueue no SOS) Este comando é enganoso; Saber quais objetos estão na fila de finalização (por si só) não é útil; Saber quais objetos estão na fila de finalização e ainda requerem finalização seria útil (existe um comando para isso?)
A supressão da finalização desativa um pouco no cabeçalho do objeto, indicando para o tempo de execução que ele não precisa ter seu Finalizer invocado (não precisa mover a fila FReachable); Ele permanece na fila de finalização (e continua a ser relatado por! FinalizeQueue no SOS)
As classes DataTable, DataSet, DataView estão todas enraizadas em MarshalByValueComponent, um objeto finalizável que pode (potencialmente) manipular recursos não gerenciados
- Como DataTable, DataSet, DataView não introduzem recursos não gerenciados, eles suprimem a finalização em seus construtores
- Embora esse seja um padrão incomum, ele evita que o chamador se preocupe em chamar Dispose após o uso
- Isso e o fato de que DataTables podem potencialmente ser compartilhados entre diferentes DataSets, é provável que os DataSets não se importem em descartar DataTables filho
- Isso também significa que esses objetos aparecerão sob o! FinalizeQueue no SOS
- No entanto, esses objetos ainda devem ser recuperados após uma única coleção, como suas contrapartes não finalizáveis
4 (novas referências):
Resposta original:
Há muitas respostas enganosas e geralmente muito ruins sobre isso - quem chegou aqui deve ignorar o barulho e ler as referências abaixo com cuidado.
Sem dúvida, Dispose deve ser chamado em qualquer objeto Finalizável.
As tabelas de dados são finalizáveis.
Chamar Dispose acelera significativamente a recuperação da memória.
MarshalByValueComponent chama GC.SuppressFinalize (this) em seu Dispose () - pular isso significa ter que esperar dezenas, senão centenas, de coleções Gen0 antes que a memória seja recuperada:
Com esse entendimento básico de finalização, já podemos deduzir algumas coisas muito importantes:
Primeiro, objetos que precisam de finalização vivem mais do que objetos que não precisam. De fato, eles podem viver muito mais tempo. Por exemplo, suponha que um objeto que esteja no gen2 precise ser finalizado. A finalização será agendada, mas o objeto ainda está no gen2, portanto, não será coletado novamente até que a próxima coleção do gen2 ocorra. Isso pode levar muito tempo e, de fato, se as coisas estiverem indo bem, vai demorar, porque as coleções gen2 são caras e, portanto, queremos que elas aconteçam com pouca frequência. Objetos mais antigos que precisam de finalização podem ter que esperar dezenas, se não centenas, de coleções gen0 antes que seu espaço seja recuperado.
Segundo, objetos que precisam de finalização causam danos colaterais. Como os ponteiros internos de objetos devem permanecer válidos, os objetos que precisam de finalização diretamente permanecerão na memória, mas tudo o que o objeto se refere, direta e indiretamente, também permanecerá na memória. Se uma grande árvore de objetos fosse ancorada por um único objeto que exigisse finalização, a árvore inteira permaneceria, potencialmente por muito tempo, como acabamos de discutir. Portanto, é importante usar os finalizadores com moderação e colocá-los em objetos que tenham o menor número possível de ponteiros internos. No exemplo da árvore que acabei de fornecer, é possível evitar facilmente o problema movendo os recursos que precisam de finalização para um objeto separado e mantendo uma referência a esse objeto na raiz da árvore.
Por fim, objetos que precisam de finalização criam trabalho para o encadeamento do finalizador. Se o seu processo de finalização for complexo, o único thread do finalizador passará muito tempo executando essas etapas, o que pode causar um atraso no trabalho e, portanto, levar mais objetos a aguardar a finalização. Portanto, é de vital importância que os finalizadores trabalhem o mínimo possível. Lembre-se também de que, embora todos os ponteiros de objeto permaneçam válidos durante a finalização, pode ser que esses ponteiros levem a objetos que já foram finalizados e, portanto, podem ser menos úteis. Geralmente é mais seguro evitar seguir os ponteiros do objeto no código de finalização, mesmo que os ponteiros sejam válidos. Um caminho de código de finalização curto e seguro é o melhor.
Suponha que alguém que tenha visto centenas de MBs de DataTables não referenciados no Gen2: isso é extremamente importante e completamente esquecido pelas respostas neste tópico.
Referências:
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry
http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/