Devo Dispose () DataSet e DataTable?

197

DataSet e DataTable implementam IDisposable, portanto, pelas práticas recomendadas convencionais, devo chamar seus métodos Dispose ().

No entanto, pelo que li até agora, o DataSet e o DataTable não possuem recursos não gerenciados, portanto Dispose () não faz muito.

Além disso, não posso usar apenas using(DataSet myDataSet...)porque o DataSet possui uma coleção de DataTables.

Portanto, para estar seguro, eu precisaria percorrer myDataSet.Tables, descartar cada DataTables e descartar o DataSet.

Portanto, vale a pena chamar Dispose () em todos os meus DataSets e DataTables?

Termo aditivo:

Para aqueles que pensam que o DataSet deve ser descartado: Em geral, o padrão para descarte é usar usingou try..finallyporque você deseja garantir que Dispose () será chamado.

No entanto, isso fica feio e rápido demais para uma coleção. Por exemplo, o que você faz se uma das chamadas para Dispose () lança uma exceção? Você o engole (que é "ruim") para poder continuar a descartar o próximo elemento?

Ou você sugere que eu chame apenas myDataSet.Dispose () e esqueça de descartar as DataTables em myDataSet.Tables?

mbeckish
fonte
9
Dispose não deve lançar nenhuma exceção. Se isso acontecer - não está bem escrito, então ... tente {some.Dispose (); } catch {} deve ser suficiente. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw
3
Percebi um aparente vazamento de memória em um dos meus aplicativos que usa muitos objetos DataSet. Eu não estava chamando .Dispose () ou usando blocos "using" para esses objetos. Então, eu analisei o código e adicionei um bloco "using" a todos os lugares em que estava criando um DataSet ou um DataTable, e pronto, agora a memória é liberada. Parece-me uma sólida indicação de que .Dispose () é, de fato, necessário para DataSet e DataTable.
Dizzy.stackoverflow

Respostas:

147

Aqui estão algumas discussões explicando por que o Dispose não é necessário para um DataSet.

Dispor ou Não Dispor? :

O método Dispose no DataSet existe SOMENTE por causa do efeito colateral da herança - em outras palavras, ele não faz nada útil na finalização.

Dispose deve ser chamado nos objetos DataTable e DataSet? inclui algumas explicações de um MVP:

O espaço para nome system.data (ADONET) não contém recursos não gerenciados. Portanto, não há necessidade de descartar nenhum deles, desde que você não tenha adicionado algo especial a ele.

Compreendendo o método Dispose e conjuntos de dados? tem um comentário da autoridade Scott Allen:

Na prática, raramente dispomos de um DataSet porque oferece pouco benefício "

Portanto, existe um consenso de que atualmente não há um bom motivo para chamar Dispose em um DataSet.

DOK
fonte
7
Os links fornecidos perderam totalmente o ponto de que DataTable é um tipo de objeto Finalizável. Por favor, veja a resposta de Nariman abaixo.
Herman
Resposta interessante, mas e SqlConnection, SqlCommand e SqlDataAdapter, o Dispose deve ser chamado explicitamente?
Willy
@ Willy Eu acho que muitas pessoas usam uma declaração de uso para IDisposables. using (SqlConnection cn = new SqlConnection (connectionString)) {using (SqlCommand cm = new SqlCommand (commandString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK
1
@ Willy sim, esses devem ser absolutamente descartados porque usam recursos não gerenciados. Se é chamado de forma explícita ou implícita, usando um usingbloco, é com você.
D # Stanley #
129

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/

Nariman
fonte
Bom ponto. Como você geralmente estrutura seu código quando você tem um DataSet com muitas DataTables? Toneladas de instruções aninhadas usando? Uma única tentativa ... finalmente para limpar tudo de uma vez?
21139 mbeckish
14
A declaração "No entanto, acontece que DataSets, DataViews, DataTables suprimem a finalização em seus construtores - é por isso que chamar Dipose () neles explicitamente não faz nada". é um non-sequitur: os dois conceitos não são amplamente relacionados; algo que suprime a finalização ainda pode fazer algo em Dispose (). Na verdade, faz mais sentido se a revertermos: Dispose () não faz nada, e é por isso que suprime a finalização no construtor, ou seja, porque não haveria nada para fazer, não quer incomodar o GC em chamar o finalizador ( que normalmente chama de descarte).
Marc Gravell
Obrigado. Essa discussão também se aplica a TableAdapters?
Bondolin 4/03
24

Você deve assumir que ele faz algo útil e chamar Dispose, mesmo que não faça nada na atualidade. Encarnações do NET Framework, não há garantia de que continuará assim em versões futuras, levando ao uso ineficiente de recursos.

Nuno
fonte
Também não há garantia de que ele implementará IDisposable no futuro. Eu concordo com você se fosse tão simples quanto usar (...), mas no caso do DataSet, parece muito trabalho para nada.
mbeckish
28
É bastante seguro supor que ele sempre implementará IDisposable. A adição ou remoção da interface é uma mudança de ruptura, enquanto a implementação de Dispose não é.
Greg Dean
5
Além disso, um provedor diferente pode ter uma implementação que realmente faz algo com o IDisposable.
27610 Matt Spradley
Sem mencionar que DataTablenão está selado - não é grande coisa quando você está fazendo new DataTable, mas é muito importante quando você aceita um DataTableargumento ou como resultado de uma chamada de método.
Luaan 9/02/16
17

Mesmo que o objeto não tenha recursos não gerenciados, o descarte pode ajudar o GC quebrando os gráficos de objetos. Em geral, se o objeto implementa IDisposable, Dispose () deve ser chamado.

Se Dispose () realmente faz alguma coisa ou não depende de uma determinada classe. No caso de DataSet, a implementação Dispose () é herdada de MarshalByValueComponent. Ele se remove do contêiner e chama o evento Disposed. O código fonte está abaixo (desmontado com o .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
dwieczor
fonte
1
De fato. Vi recentemente algum código em que muitas DataTables foram criadas em um loop muito grande sem serem descartadas. Isso levou a que toda a memória fosse consumida no computador e o processo travou devido à falta de memória. Depois que eu disse ao desenvolvedor para ligar para descartar a DataTable, o problema desapareceu.
RichardOD 27/05
7

Você mesmo cria as DataTables? Como a iteração através dos filhos de qualquer Objeto (como no DataSet.Tables) geralmente não é necessária, pois é dever do pai descartar todos os seus membros filhos.

Geralmente, a regra é: Se você a criou e implementa IDisposable, Dispose it. Se você NÃO o criou, NÃO o descarte, esse é o trabalho do objeto pai. Mas cada objeto pode ter regras especiais, consulte a documentação.

Para o .net 3.5, ele diz explicitamente "Descarte-o quando não estiver mais em uso", e é isso que eu faria.

Michael Stum
fonte
4
Pelo que entendi, o consenso geral é que um objeto deve dispor de seus próprios recursos não gerenciados. No entanto, em geral, uma coleção de objetos IDisposable não percorre seus elementos para descartar cada um, porque pode haver outras referências a seus elementos fora da coleção: stackoverflow.com/questions/496722/…
mbeckish
1
É verdade que coleções são sempre algo que considero especial, porque geralmente não estão "fazendo" nada, são apenas ... Contêineres, então nunca me preocupei com isso.
Michael Stum
7

Chamo de disposição a qualquer momento que um objeto implementa IDisposeable. Está lá por uma razão.

Os conjuntos de dados podem ser grandes quantidades de memória. Quanto mais cedo eles forem marcados para limpeza, melhor.

atualizar

Faz cinco anos desde que eu respondi a essa pergunta. Eu ainda concordo com a minha resposta. Se houver um método de descarte, ele deverá ser chamado quando você terminar o objeto. A interface IDispose foi implementada por um motivo.

Chuck Conway
fonte
5
Chamar o descarte não acelera a recuperação da memória; para isso, você teria que iniciar manualmente o coletor de lixo, que geralmente é um plano ruim.
Tetraneutron
2
Se Dispose definir um monte de referências como nulo, poderá fazer com que os objetos sejam candidatos à coleção que, de outra forma, poderiam ser ignorados.
22611 Greg Dean #
1
O objetivo do Dispose não é limpar a memória dos objetos gerenciados - esse é o trabalho do coletor de lixo. O objetivo é limpar objetos não gerenciados. Parece haver evidências de que os DataSets não têm nenhuma referência não gerenciada, portanto, teoricamente, não é necessário descartá-las. Dito isto, nunca estive em uma situação em que tive que sair do meu caminho para chamar Dispose - eu chamaria assim mesmo.
CBP
4
O principal uso do IDisposable é liberar recursos não gerenciados. Muitas vezes, também modifica o estado de uma maneira que faz sentido para uma instância descartada. (ou seja, propriedades definidas para falso, referências definido como nulo, etc.)
Greg Dean
3
Se houver um método de descarte em um objeto, ele foi colocado lá por um motivo, independentemente de ser para limpar objetos não gerenciados ou não.
amigos estão dizendo sobre chuck
4

Se sua intenção ou o contexto desta pergunta for realmente uma coleta de lixo, você poderá definir os conjuntos de dados e tabelas de dados para nulos explicitamente ou usar a palavra-chave using e deixá-los fora do escopo. Dispose não faz muito como Tetraneutron disse anteriormente. O GC coletará objetos do conjunto de dados que não são mais referenciados e também aqueles que estão fora do escopo.

Eu realmente gostaria que as pessoas forçadas a votar a escrever um comentário antes de votar na resposta.

Srikar Doddi
fonte
+ 1 Acho que algumas pessoas não querem permitir que outras pessoas considerem pontos de vista diferentes.
DOK
2
voto negativo, de forma alguma impede que as pessoas considerem pontos de vista diferentes.
Greg Dean
1

Os conjuntos de dados implementam MarshalByValueComponent completo com IDisposable, que implementa IDisposable. Como os conjuntos de dados são gerenciados, não há benefício real em chamar o descarte.

Tetraneutron
fonte
6
Pode agora, quem sabe o que fará depois.
267 Greg Greg Dean
Essa atitude na qual você especula que qualquer código não fará no futuro o que deveria fazer é uma suposição para todos os envolvidos.
MicroservicesOnDDD
0

Tente usar a função Clear (). Funciona muito bem para mim para descartar.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
Hasan Savran
fonte
0

Não há necessidade de Dispose () porque DataSet herda a classe MarshalByValueComponent e MarshalByValueComponent implementa a interface IDisposable

Sunil Dhappadhule
fonte