trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

EFFECTIVE C# 6.0/7.0 読書メモ 項目17

項目17 標準的なDisposeパターンを実装する

アンマネージリソースを持つ型のメモリ管理について。標準的な方法(Disposeパターン)を実装すること。

Disposeパターンの利点

型のユーザはIDisposableインターフェイスによりアンマネージリソースを適切なタイミングで解放できる*1。解放し忘れた場合も、ファイナライザ(デストラクタ)により非マネージドリソースを解放できる*2。また、IDisposableインターフェイスにより解放した場合、パフォーマンスの低下を最小限に抑えることができる*3

Disposeパターン詳細

docs.microsoft.com

ファイナライザの注意点
  • 非マネージリソースを扱う場合に限りファイナライザを実装すること。ファイナライザがあるだけでコストになる(GCの処理が増える)
  • ファイナライザではリソースの解放のみ行うこと。それ以外の処理を行うと、オブジェクトの参照を保持し、生存期間を延長させてしまうことがある。そうなると将来的にバグを生む可能性が高い。
ファイナライザを持つ型に対するGCの処理*4
  1. ファイナライザを持つオブジェクトをファイナライザキューへ追加
  2. ファイナライザキューに追加したオブジェクトのファイナライザを呼び出す新しいスレッドを開始
    (この時点ではメモリから削除されておらず、GCを生き残ったので世代が上がる)
  3. ファイナライザを実行し終えていたら、ファイナライザが不要なオブジェクトとしてマーク
  4. 上位の世代に対するガベージコレクションが実行された時点でオブジェクトが削除される
その他

親クラスでファイナライザをオーバーライドしていれば派生クラスでのオーバーライドは不要か(親クラスのファイナライザが実行され、派生クラスのDispose(bool)が呼ばれるため)

*1:マネージリソースに対しても同様

*2:ファイナライザはアンマネージリソースが確実に解放されるようにするための唯一の方法

*3:Dispose()メソッド内でGC.SuppressFinalize(this)を呼び出す。これは指定したオブジェクトに対してGCがファイナライザを呼び出さないように要求する処理

*4:Dispose()内でGC.SuppressFinalize(this)が呼ばれてない場合