C#ではGCによってメモリ管理が自動化されていますが、それでもGCの管理外にあるリソースを意識しなければならない場面は少なくありません。これがいわゆる「アンマネージドリソース」です。
GCは非常に強力ですが、アプリケーションが扱うすべてのリソースを管理しているわけではありません。この点を理解していないと、「GCがあるのにリソースが解放されない」といった問題に直面することがあります。
アンマネージドリソースとは何か
一言でまとめると、CLR(GC)が管理していないリソース です。 これらは主にOSや外部システムに存在しており、.NETの外側にある資源と考えると理解しやすいです。
具体例
アンマネージドリソースには、例えば次のようなものがあります。
OSや外部に存在するリソース
- ファイルハンドル(File)
- ソケット(TCP / UDP)
- DB接続(コネクション)
- ウィンドウハンドル(Win32)
- GDIオブジェクト
- ネイティブメモリ(malloc / VirtualAlloc)
共通点
- CLRが中身を理解していない
- GCの管理対象外である
- OSやネイティブ側に実体がある
マネージドリソースとの違い
マネージドリソースとアンマネージドリソースの違いは、「誰が管理しているか」にあります。
| 種類 | 管理者 | 例 |
|---|---|---|
| マネージド | CLR(GC) | string, List, class |
| アンマネージド | OS / ネイティブ | file, socket, handle |
この違いは非常に重要で、次のように整理できます。
- マネージド:CLRが自動的にライフサイクルを管理する
- アンマネージド:自動管理されない(CLRの対象外)
GCは何を見ているのか
GCは万能に見えますが、実際には非常にシンプルな判断基準で動いています。
GCが見ているもの
- オブジェクトへの参照が存在するかどうか
- 参照がなければ回収する
つまり、「メモリとして不要かどうか」だけを判断している というわけです。
具体例で理解する
例えば、次のコードを考えます。
var stream = new FileStream("test.txt", FileMode.Open);
このコードの裏側では、次のような処理が行われています。
- OSにファイルオープンを依頼する
- OSがファイルハンドルを発行する
- C#のオブジェクトがそのハンドルを保持する
ここで重要なのは、「C#オブジェクト」と「実体」が別であるという点です。
FileStream(マネージドオブジェクト)← C#オブジェクト
↓
OSのファイルハンドル(アンマネージド)← 実体
GCが見ているのは、あくまで上の「FileStreamオブジェクト」のみです。 FileStreamオブジェクトが参照されているかどうかしか見ていません。
しかし実際には、次のような状態になっています。
- OS側ではファイルが開かれている
- ハンドルも保持されている
GCは「ファイルが開いていること」を認識していません。 このズレが、アンマネージドリソースの理解において重要なポイントになります。
イメージで整理
マネージドメモリ
[ CLRヒープ ]
├─ object A
├─ object B
GCが追跡して自動回収します。
アンマネージドリソース
[ OS ]
├─ file handle
├─ socket
├─ DB connection
CLRの外側にあり、GCの管理対象外です。
■ よくある誤解
GCがあるからすべて自動で安全
これは誤解です。実際は以下の通りです。
- GCが管理するのは「メモリ」だけ
- OSリソースは対象外
放置してもそのうち解放される
このような考え方も危険です。実際には以下の問題があります。
- 解放タイミングは保証されない
- その間リソースは保持され続ける
結果として、リソースの枯渇につながる可能性があります。
まとめ
アンマネージドリソースとは
- CLRが管理していないリソース
- OSや外部に存在する
マネージドとの違い
- マネージド:GCが自動管理
- アンマネージド:GCの管理対象外
本質的なポイント
- GCはメモリしか見ていない
- OSリソースの状態は認識しない
意識すべきこと
- メモリ管理とリソース管理は別物である
マネージドリソースとアンマネージドリソースの違いを正しく理解しておくことで、「GCがあるのにリソースが枯渇する」といった一見不思議に見える問題の背景が理解できるようになります。