C++のデストラクタの感覚でC#のファイナライザもプログラム終了時に呼び出されると思い込んでいたのですが、ファイナライザは基本的に呼び出されません。呼び出されるのはGCの実行時に対象となった場合のみです。Windowsのプロセス終了時に関連リソースを解放する設計によりほとんどの場合は終了処理の高速化につながりますが、ファイナライザでファイルへの書き込みやExcel COMを閉じる設計だと想定外の動作につながります。
確実な解放処理にはIDisposable
やIAsyncDisposable
が重要だと再確認しました。
具体的には次のコードを実行するとコンストラクタしか呼び出されないことが分かります。null
を代入したりGC.Collect
を呼び出したりしても同じです。
var tc1 = new TestClass1(); class TestClass1 { public TestClass1() { Console.WriteLine("TestClass1"); } ~TestClass1() { Console.WriteLine("~TestClass1"); } }
出力
TestClass1
ちなみにサイズの大きなフィールドを持つ場合はprotected void Dispose(bool disposing)
の実装とdisposing == true
時の参照カウンタ減少 (フィールドへのnull
の代入)が有効です。以下のQ&Aにもある通り、大きなフィールドは参照カウントを減らさないとインスタンスがGCに回収されるまでメモリ上に残ってしまいます。