finalizador não chamado após GC.Collect () [duplicate]
Acho que estou perdendo algo fundamental e espero que você possa ajudar. O código abaixo cria um objeto, remove a referência e chama o coletor de lixo. Minha expectativa era que o finalizador de SomeClass fosse chamado quando estivesse em Readline. Não é verdade. Tentei chamar GC.Collect em um loop, adicionando algumas chamadas Sleep () para que o thread do finalizador fosse iniciado. Não acontece.
Somente quando o Main termina o finalizador é atingido, mas surpreendentemente é atingido duas vezes. O que estou perdendo?
class Program
{
public static void Main(string[] args)
{
SomeClass some = new SomeClass("Hello World!");
some = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Done");
Console.ReadLine();
}
}
class SomeClass
{
string ss;
public SomeClass(string s) { ss = s; }
~SomeClass()
{
var hash = this.GetHashCode();
}
}
Adendo Há uma diferença entre executar um programa no modo de depuração e no modo de liberação. O programa a seguir produz no modo de depuração, Start - Done - Finalize
enquanto no modo de liberação o arquivo de log é exibido Start - Finalize - Done
. Este último é o que eu esperava.
class Program
{
private static string logfile = @"c:\temp\log.txt";
public static void Main(string[] args)
{
File.WriteAllText(logfile, "Start\n");
SomeClass some = new SomeClass("Hello World!");
some = null;
GC.Collect();
GC.WaitForPendingFinalizers();
File.AppendAllText(logfile, "Done\n");
}
}
class SomeClass
{
private static string logfile = @"c:\temp\log.txt";
public string SomeString { get; set; }
public SomeClass(string s) { SomeString = s; }
~SomeClass()
{
File.AppendAllText(logfile, "Finalize\n");
}
}
Respostas
Objetos que são coletados como lixo se tornam adequados para finalização e são colocados em uma fila de finalização. Não há absolutamente nenhuma garantia de que esses finalizadores serão executados .
Vou redirecioná-lo para as ótimas postagens de Eric Lippert sobre isso, apropriadamente chamadas de "Quando tudo o que você sabe está errado." Em particular:
Mito: definir uma variável como nula faz com que o finalizador seja executado no objeto que foi referenciado anteriormente pela variável. Definir uma variável como nula não faz com que nada aconteça imediatamente, exceto alterar o valor da variável. Se a variável foi a última referência viva ao objeto em questão, então esse fato será descoberto quando o coletor de lixo executar o coletor para qualquer geração em que o objeto estava. (Se for executado, pode não ser. Não há garantia que o GC executa.)
E mesmo com GC.Collect()
:
Mito: a chamada
GC.Collect()
faz com que os finalizadores sejam executados. Não, isso faz com que aconteça uma coleta. Isso pode identificar objetos que são candidatos à finalização, mas não força o encadeamento do finalizador a ser agendado. Se é isso que você quer - apenas para fins de teste, por favor! - então ligueGC.WaitForPendingFinalizers()
.
Portanto, a melhor chance de sucesso é com GC.WaitForPendingFinalizers()
. Mas mesmo assim, referindo-se ao resto do post ( e sua sequência ), FINALIZADORES NÃO TÊM GARANTIA DE EXECUÇÃO . Não dependa disso.
Escrevi uma explicação mais detalhada sobre essa questão para que você possa usá-la como ponto de partida para pesquisas futuras.