[C#][.NET] ガベージコレクタ/ガベージコレクション(GC)に関するまとめ

個人プロジェクトで気軽に C# + WPF を使ってみたら、メモリリークに手ひどくやられたので勉強まとめ

# .NETにおけるGCの動作について
以下の記事が詳しいのでおすすめです。

[.NETにおけるマネージヒープとガベージコレクション - Qiita](http://qiita.com/mima_ita/items/8303f2a476e8630f0728)

# 用語
## ガベージコレクタ/ガベージコレクション
> ガベージコレクション（英語: garbage collection; GC）とは、プログラムが動的に確保したメモリ領域のうち、不要になった領域を自動的に解放する機能である。「ガベージコレクション」を直訳すれば「ゴミ集め」「ごみ拾い」となる。1959年ごろ、LISPにおける問題を解決するためジョン・マッカーシーによって発明された[1][2]。
メモリの断片化を解消する機能はコンパクションと呼ばれ、実現方法によってはガベージコレクションと共にコンパクションも行う仕組みになっている。そのためコンパクションを含めてガベージコレクションと呼ぶ場合もあるが、厳密には区別される。
また、ガベージコレクションを行う主体はガベージコレクタと呼ばれる。ガベージコレクタはタスクやスレッドとして実装される場合が多い。
参照カウント方式のガベージコレクションは通常煩雑なコーディングを必要とするが、それを必要なく実装したライブラリとしスマートポインタがある。
引用: [ガベージコレクション - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)

## マネージリソース
別名: マネージヒープ, マネージメモリ

GCの管理下にあるリソース。通常のC#での実装はこちらに該当する。

## アンマネージリソース
別名: アンマネージヒープ, アンマネージメモリ

GCの管理下にないリソース。ファイルハンドルやウィンドウハンドル、データベース接続などが該当する。
要はあえてアンマネージリソースを取得するAPIを使用している(AllocCoTaskMemなどでの明示的なAlloc/Freeの使用）、またはCやC++などのライブラリをC#から使用している場合に内部でメモリ確保されたリソースはアンマネージリソースとなる。
アンマネージリソースを取り扱うAPIは、リソース解放のため明示的にCloseや、Disposeを呼ぶ必要があるケースが多い。

[AllocHGlobalとAllocCoTaskMem　どちらを使うべきか？ - Qiita](http://qiita.com/Nuits/items/9dc67cb12e2dcf8d09bd)

# リーク/GCの未回収への対策
## 循環参照問題
Q. 親子関係のあるオブジェクトで相互に参照を持ちたいときに循環参照になるのを回避するには？
A. 弱参照を使う

[[C#][.NET 4.5~] WeakReference<T>/弱参照の使い方 - Qiita](http://qiita.com/koara-local/items/0e4b1b8e2fc97141fcc0)

## アンマネージドリソースを直接扱うクラスのため、明示的にClose/Disposeが呼ばれないと解放されない(例:StreamReader)
Q. 例外処理とか、Closeの実装忘れとかで解放されていない
A. `using` を使える場合は常に使うようにする

使用側:
[using ステートメント (C# リファレンス)](https://msdn.microsoft.com/ja-jp/library/yh598w02.aspx)
[C# Tips －usingを使え、使えったら使え(^^)－](http://divakk.co.jp/aoyagi/csharp_tips_using.html)

実装側:
[やりがちなアンマネージドメモリのリークと対処方法 - Qiita](http://qiita.com/Nuits/items/9ce6a205a50bf8e3dbeb)

## 明示的なGCの呼び出しについて
Q. 明示的に `System.GC.Collect()` を呼ぶのは有効？
A. gen0に対する回収は高頻度でよばれているため、デバッグ用途以外で明示的に呼ぶのは特に有効ではないらしい。ただし、`System.GC.Collect()` はすべての世代に対して回収を実施するため、gen2まで回収させたい場合には有効？

仮に明示的にGCを実行する場合は以下のようにする。

```csharp:明示的なGCの呼び出し
System.GC.Collect(); // アクセス不可能なオブジェクトを除去
System.GC.WaitForPendingFinalizers(); // ファイナライゼーションが終わるまでスレッド待機
System.GC.Collect(); // ファイナライズされたばかりのオブジェクトに関連するメモリを開放
```
参考: [.NET アプリケーションのパフォーマンスとスケーラビリティの向上 - 第 5 章 「マネージ コ ード パフォーマンスの向上」](https://msdn.microsoft.com/ja-jp/library/ms998547.aspx#scalenetchapt05_topic10)

TODO: 要調査
85KB以上のサイズのオブジェクトは世代別管理のヒープ（generation 0）ではなく、LOH(Large Object Heap)に割り当てられて、扱いとしてはgen2の扱いで回収されるらしい。なので、明示的に `System.GC.Collect()` 呼ぶのも意味がないわけでもない？（未調査）

.NET Framework 4.5.1から、GC時のLOHのコンパクションの有効化オプションが追加された（ただしかなり遅くなるらしい）
[GCSettings.LargeObjectHeapCompactionMode プロパティ (System.Runtime)](https://msdn.microsoft.com/ja-jp/library/system.runtime.gcsettings.largeobjectheapcompactionmode(v=vs.110).aspx)

## WPFめっちゃリークする問題
Q. WPFのリークの解決方法
A. 調査中（なにかわかったら問題別に記事を書きます・・・）

# 関連
[[C#][.NET] オブジェクトの世代別ガベージコレクションでの世代番号を確認する - Qiita](http://qiita.com/koara-local/items/5ed703f9ce310b58368f)

# 参考
[自動メモリ管理](https://msdn.microsoft.com/ja-jp/library/f144e03t(v=vs.110).aspx)
[.NET アプリケーションのパフォーマンスとスケーラビリティの向上 - 第 5 章 「マネージ コ ード パフォーマンスの向上」](https://msdn.microsoft.com/ja-jp/library/ms998547.aspx#scalenetchapt05_topic10)
[.NETにおけるマネージヒープとガベージコレクション - Qiita](http://qiita.com/mima_ita/items/8303f2a476e8630f0728)
[ガベージコレクション、マネージリソース、アンマネージリソース、Dispose、Finalize - goungoun技術系雑記帳](http://goungoun.dip.jp/app/fswiki/wiki.cgi/devnotebook?page=%A5%AC%A5%D9%A1%BC%A5%B8%A5%B3%A5%EC%A5%AF%A5%B7%A5%E7%A5%F3%A1%A2%A5%DE%A5%CD%A1%BC%A5%B8%A5%EA%A5%BD%A1%BC%A5%B9%A1%A2%A5%A2%A5%F3%A5%DE%A5%CD%A1%BC%A5%B8%A5%EA%A5%BD%A1%BC%A5%B9%A1%A2Dispose%A1%A2Finalize)