Quando devo usar GC.SuppressFinalize ()?


287

No .NET, em quais circunstâncias devo usar GC.SuppressFinalize()?

Que vantagem (s) usando esse método me oferece?


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 é:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(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;
        }
    }

    public void Dispose() // 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.

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif

1
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().


2

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.

Consulte: Método GC.SuppressFinalize (Object) - Microsoft Docs


9
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.


0

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.