Tenho visto algumas perguntas sobre finalizadores e IDisposable, stackoverflow também deve ter alguma coisa sobre GC.SupressFinalize e referências fracas
Sam Saffron
Não acho que referências fracas façam muito em relação aos finalizadores - talvez você deva postar uma pergunta mais direta sobre eles.
Michael Burr
Yerp, eu pretendia postar uma pergunta separada sobre referências fracas, tudo isso pode se vincular quando você cria pools de objetos. Também devo fazer uma pergunta sobre objeto renascimento ala ReRegisterForFinalize
Sam Saffron
Respostas:
296
SuppressFinalizesó deve ser chamado por uma classe que tenha um finalizador. Ele está informando ao Garbage Collector (GC) que o thisobjeto foi totalmente limpo.
O IDisposablepadrão recomendado quando você tem um finalizador é:
publicclassMyClass:IDisposable{privatebool disposed =false;protectedvirtualvoidDispose(bool disposing){if(!disposed){if(disposing){// called via myClass.Dispose(). // OK to use any private object references}// Release unmanaged resources.// Set large fields to null.
disposed =true;}}publicvoidDispose()// Implement IDisposable{Dispose(true);
GC.SuppressFinalize(this);}~MyClass()// the finalizer{Dispose(false);}}
Normalmente, o CLR mantém abas nos objetos com um finalizador quando eles são criados (tornando-os mais caros de criar). SuppressFinalizeinforma ao GC que o objeto foi limpo corretamente e não precisa ir para a fila do finalizador. Parece um destruidor de C ++, mas não age como um.
A SuppressFinalizeotimização não é trivial, pois seus objetos podem ficar muito tempo aguardando na fila do finalizador. Não fique tentado a chamar SuppressFinalizeoutros objetos, lembre-se. Esse é um defeito sério esperando para acontecer.
As diretrizes de design nos informam que um finalizador não é necessário se o seu objeto for implementado IDisposable, mas se você tiver um finalizador, deverá implementá-lo IDisposablepara permitir a limpeza determinística da sua classe.
Na maioria das vezes você deve se safar IDisposablepara limpar recursos. Você só precisa de um finalizador quando seu objeto se apega a recursos não gerenciados e precisa garantir que esses recursos sejam limpos.
Nota: Às vezes, os codificadores adicionam um finalizador para depurar construções de suas próprias IDisposableclasses, a fim de testar se o código descartou seu IDisposableobjeto corretamente.
publicvoidDispose()// Implement IDisposable{Dispose(true);#if DEBUG
GC.SuppressFinalize(this);#endif}#if DEBUG~MyClass()// the finalizer{Dispose(false);}#endif
No primeiro trecho de código, estou publicando como é o padrão recomendado IDisposable + finalizer. O código de depuração é bom, mas pode ser uma distração. .. Só posso recomendar evitar finalizadores, exceto para classes que possuem recursos não gerenciados. Escrever código finalizador seguro não é trivial.
Robert Paulson
1
Oi, Por que precisamos chamar dispor com false como parâmetro do finalizador? E se o descarte nunca for chamado e depois não for descartado? E se apenas verificarmos se o objeto foi descartado ou não e fizermos a limpeza real.
Dreamer
3
@ Sonhador - isso depende da sua implementação. Em geral, você deseja saber se Dispose está sendo chamado pelo finalizador versus a implementação IDisposable.Dispose (). Se chamado do finalizador, você deve assumir que as referências privadas não são mais válidas e realmente não pode fazer muito. Se, no entanto, for chamado a partir de IDisposable.Dispose (), você saberá que as referências ainda são válidas.
Robert Paulson
32
Se a implementação da classe IDisposablenão for sealed, ela deve incluir a chamada GC.SuppressFinalize(this)mesmo que não inclua um finalizador definido pelo usuário . Isso é necessário para garantir a semântica adequada para tipos derivados que adicionam um finalizador definido pelo usuário, mas substituem apenas o Dispose(bool)método protegido .
Sam Harwell
1
Não ser sealedcomo mencionado por @SamHarwell é importante para as classes derivadas. O CodeAnalysis resulta em ca1816 + ca1063 quando a classe não é selada, mas as classes seladas são boas sem SuppressFinalize.
dashesy
38
SupressFinalizeinforma ao sistema que qualquer trabalho que tenha sido realizado no finalizador já foi realizado, portanto, o finalizador não precisa ser chamado. Nos documentos do .NET:
Objetos que implementam a interface IDisposable podem chamar esse método a partir do método IDisposable.Dispose para impedir que o coletor de lixo chame Object.Finalize em um objeto que não o exija.
Em geral, a maioria dos Dispose()métodos deve ser capaz de chamar GC.SupressFinalize(), porque deve limpar tudo o que seria limpo no finalizador.
SupressFinalizeé apenas algo que fornece uma otimização que permite ao sistema não incomodar na fila do objeto no encadeamento do finalizador. Um Dispose()finalizador / gravado corretamente deve funcionar corretamente com ou sem uma chamada para GC.SupressFinalize().
Esse método deve ser chamado no Disposemétodo de objetos que implementa o IDisposable, assim o GC não chamaria o finalizador outra vez se alguém chamar o Disposemétodo.
Eu acho que "Must" está errado - nem mesmo "deveria" - é apenas que, em alguns cenários, você pode eliminar a sobrecarga de enfileirar / finalizar o objeto.
Básico
1
Dispose(true);
GC.SuppressFinalize(this);
Se o objeto tiver finalizador, .net colocará uma referência na fila de finalização.
Desde que ligamos Dispose(ture) , ele limpa o objeto, portanto não precisamos da fila de finalização para fazer este trabalho.
Então, GC.SuppressFinalize(this)remova a referência na fila de finalização.
Se uma classe, ou qualquer coisa dela derivada, puder conter a última referência ao vivo para um objeto com um finalizador, um GC.SuppressFinalize(this)ou GC.KeepAlive(this)deve ser chamado no objeto após qualquer operação que possa ser afetada adversamente por esse finalizador, garantindo assim que o finalizador vence só será executado após a conclusão da operação.
O custo de GC.KeepAlive()e GC.SuppressFinalize(this)é essencialmente o mesmo em qualquer classe que não tenha um finalizador, e as classes que têm finalizadores geralmente devem chamar GC.SuppressFinalize(this); portanto, Dispose()nem sempre é necessário usar a última função como a última etapa de , mas não será necessário. estar errado.
Respostas:
SuppressFinalize
só deve ser chamado por uma classe que tenha um finalizador. Ele está informando ao Garbage Collector (GC) que othis
objeto foi totalmente limpo.O
IDisposable
padrão recomendado quando você tem um finalizador é:Normalmente, o CLR mantém abas nos objetos com um finalizador quando eles são criados (tornando-os mais caros de criar).
SuppressFinalize
informa ao GC que o objeto foi limpo corretamente e não precisa ir para a fila do finalizador. Parece um destruidor de C ++, mas não age como um.A
SuppressFinalize
otimização não é trivial, pois seus objetos podem ficar muito tempo aguardando na fila do finalizador. Não fique tentado a chamarSuppressFinalize
outros objetos, lembre-se. Esse é um defeito sério esperando para acontecer.As diretrizes de design nos informam que um finalizador não é necessário se o seu objeto for implementado
IDisposable
, mas se você tiver um finalizador, deverá implementá-loIDisposable
para permitir a limpeza determinística da sua classe.Na maioria das vezes você deve se safar
IDisposable
para limpar recursos. Você só precisa de um finalizador quando seu objeto se apega a recursos não gerenciados e precisa garantir que esses recursos sejam limpos.Nota: Às vezes, os codificadores adicionam um finalizador para depurar construções de suas próprias
IDisposable
classes, a fim de testar se o código descartou seuIDisposable
objeto corretamente.fonte
IDisposable
não forsealed
, ela deve incluir a chamadaGC.SuppressFinalize(this)
mesmo que não inclua um finalizador definido pelo usuário . Isso é necessário para garantir a semântica adequada para tipos derivados que adicionam um finalizador definido pelo usuário, mas substituem apenas oDispose(bool)
método protegido .sealed
como mencionado por @SamHarwell é importante para as classes derivadas. O CodeAnalysis resulta em ca1816 + ca1063 quando a classe não é selada, mas as classes seladas são boas semSuppressFinalize
.SupressFinalize
informa ao sistema que qualquer trabalho que tenha sido realizado no finalizador já foi realizado, portanto, o finalizador não precisa ser chamado. Nos documentos do .NET:Em geral, a maioria dos
Dispose()
métodos deve ser capaz de chamarGC.SupressFinalize()
, porque deve limpar tudo o que seria limpo no finalizador.SupressFinalize
é apenas algo que fornece uma otimização que permite ao sistema não incomodar na fila do objeto no encadeamento do finalizador. UmDispose()
finalizador / gravado corretamente deve funcionar corretamente com ou sem uma chamada paraGC.SupressFinalize()
.fonte
Esse método deve ser chamado no
Dispose
método de objetos que implementa oIDisposable
, assim o GC não chamaria o finalizador outra vez se alguém chamar oDispose
método.Consulte: Método GC.SuppressFinalize (Object) - Microsoft Docs
fonte
Se o objeto tiver finalizador, .net colocará uma referência na fila de finalização.
Desde que ligamos
Dispose(ture)
, ele limpa o objeto, portanto não precisamos da fila de finalização para fazer este trabalho.Então,
GC.SuppressFinalize(this)
remova a referência na fila de finalização.fonte
Se uma classe, ou qualquer coisa dela derivada, puder conter a última referência ao vivo para um objeto com um finalizador, um
GC.SuppressFinalize(this)
ouGC.KeepAlive(this)
deve ser chamado no objeto após qualquer operação que possa ser afetada adversamente por esse finalizador, garantindo assim que o finalizador vence só será executado após a conclusão da operação.O custo de
GC.KeepAlive()
eGC.SuppressFinalize(this)
é essencialmente o mesmo em qualquer classe que não tenha um finalizador, e as classes que têm finalizadores geralmente devem chamarGC.SuppressFinalize(this)
; portanto,Dispose()
nem sempre é necessário usar a última função como a última etapa de , mas não será necessário. estar errado.fonte