マネージリソースとアンマネージリソースの定義

先日、MSDN のフォーラム、マネージリソースのみで構成されているクラスにIDisposeを実装するメリット を読んでいたら、いささか引っかかるまとめが書いてありました。

「いつものパン」があなたを殺す: 脳を一生、老化させない食事 (単行本)

「いつものパン」があなたを殺す: 脳を一生、老化させない食事 (単行本)

リソースという用語は例えば次のように使われる。
・double[],string等 → 通常リソースとは言わない。
・Stream,Bitmap等  → マネージドリソース。
ポインター等    → 非マネージリソース。


マネージリソースのみで構成されているクラスにIDisposeを実装するメリット


それまで静観モードだったのですが、これはちと違うんではないかい?ということで私も参戦したところ、さらに議論が膨らんで、ますます「マネージリソース」と「アンマネージリソース」の定義が判らなくなりました。

そこで思い出したのが、bleis さんのこの記事。

カプセル化、情報隠蔽、データ隠蔽

私もこれにならって、様々な文献や記事から「マネージリソース」と「アンマネージリソース」の定義について探ってみたいと思います。



方向性

  • 「マネージリソース」とは何か
  • 「アンマネージリソース」とは何か

「マネージドリソース」というのは .NET Framework において MS が提唱した概念なので(と推測する)、まずは MSDN もしくは Microsoft の技術者がこれ等の言葉をどう用いているかを参考にした方がよさそうです。ただし手持ちの書籍が少ないため、ネットからの引用が多くなることをご容赦願います。

注意事項

「マネージリソース」という用語で検索すると、「リソースファイル(.resx) に含まれるリソース」もマネージリソースと呼ぶようです。ただしこちらは狭義の意味になり混乱を招くので、ここでは取り上げません。

.NET のクラスライブラリ設計

CLR は自動的なメモリ管理に対するサポートを提供します。マネージメモリ (C# の new 演算子を使用して割り当てられたメモリ) を明示的に開放する必要はありません。ガベージコレクタ (GC:Garbage Collector) によって自動的に解放されます。これはメモリの解放という、退屈で複雑なタスクから開発者を解放するもので、.NET Framework による生産性向上の主要な理由の一つです。


.NET のクラスライブラリ設計(P274)


ここでは、マネージリソースを「C# の new 演算子を使用して割り当てられたメモリ」と言ってますね。これに倣うなら string のインスタンスも当然マネージリソースになると思います。

残念なことに、マネージメモリは数多くの種類のシステムリソースに過ぎません。マネージメモリ以外のリソースは、依然として明示的に解放される必要があり、「アンマネージリソース」と呼ばれます。GC はそのようなアンマネージリソースを管理するために特別に設計されているわけではありません。これはつまり、アンマネージリソースを管理する責任は開発者側にあるということを意味しています。


.NET のクラスライブラリ設計(P275)


マネージメモリ以外のリソースを総じて「アンマネージリソース」と呼んでます。これに続くコラムの中で Brian・Pepin と Joe・Duffy の二人がなかなか興味深いことを言ってますが、本稿の趣旨から微妙に外れるため取り上げません。興味のある方はぜひ買って読んでみてください。


.NETのクラスライブラリ設計 (Microsoft.net Development Series)

.NETのクラスライブラリ設計 (Microsoft.net Development Series)


MCTS スキルチェック問題集 70-536 Microsoft .NET Framework 2.0 アプリケーション開発基礎

IDisposable インターフェイスの Dipose メソッドは、このインターフェイスを実装するクラスのインスタンスが保持する、ファイル、ストリーム、ハンドルなどのアンマネージリソースを閉じたり解放したりするよう実装します。


MCTS スキルチェック問題集 70-536 Microsoft .NET Framework 2.0 アプリケーション開発基礎(P55)


MS の公式解説書&問題集でありますが、ここではファイル、ストリーム、ハンドルなどを端的に「アンマネージリソース」と呼んでます。


MCTSスキルチェック問題70-536.NET FRAMEWORK2.0アフリ開発基礎 (マイクロソフト公式解説書)

MCTSスキルチェック問題70-536.NET FRAMEWORK2.0アフリ開発基礎 (マイクロソフト公式解説書)

  • 作者: トニーノースラップ,ビルライアン,ショーンワイルダマス,Tony Northrup,Bill Ryan,Shawn Wildermuth,山崎明子
  • 出版社/メーカー: 日経BP
  • 発売日: 2007/08/30
  • メディア: 単行本
  • クリック: 39回
  • この商品を含むブログ (9件) を見る


.NET エンタープライズ Webアプリケーション 開発技術大全 Vol.5 トランザクション

  • ファイルを操作するオブジェクト
  • ネットワーク I/O 処理を行うオブジェクト
  • データベースコネクションオブジェクト

GC による自動メモリ解放では、実際にインスタンスが利用されなくなってから物理的に破棄されるまでの間にタイムラグが生じる。しかし、上述したようなオブジェクトは、内部的にはいずれもシステムの共有リソースを利用しており、解放が遅延してしまうと、共有リソースのロックや枯渇による例外が発生する恐れがある。このため、こうしたオブジェクトに関しては、利用後にアプリケーションプログラマが明示的に解放するようなプログラミングが求められる。
・・・・・・
こうした共有リソースのほとんどは、アンマネージリソースである。アンマネージリソースとは Windows OS が管理するファイルハンドルやネットワークソケットなどのリソースのことであるが、こうしたリソースは CLR が直接管理していない。


.NET エンタープライズ Webアプリケーション 開発技術大全 Vol.5 トランザクション編(P193)


MS の赤間さんの本です。ここでは「共有リソース」を「アンマネージリソース」と呼んでますね。このページではサービスコンポーネントの解放について触れてますが、インスタンスが不要になったら必ず Dispose を呼び出せと書いております。

・・・しかし、ここまでの流れで、「マネージリソース」の定義に関する話は一箇所しか発見できない。



CLR 徹底解剖 IDisposable について

public class Database : IDisposable
{
    private NetworkConnection connection;
    private IntPtr fileHandle;

    // ...
}

public class NetworkConnection : IDisposable
{
    private IntPtr connectionHandle;

    // ...
}


・・・・この原則を一般化すると、Dispose メソッドでは、オブジェクトが保持しているすべてのリソースを、マネージ オブジェクトかネイティブ リソースかに関わらず、安全にクリーンアップできるということになります。 しかし、ファイナライザではファイナライズ可能でないオブジェクトだけを安全にクリーンアップでき、一般的にはファイナライザがネイティブ リソースだけを解放するようにする必要があります。
この例で興味深いのは、connection フィールドが明らかにネイティブな接続ハンドルを含んでいるにもかかわらず、ネイティブ リソースと見なされないという点です。 NetworkConnection クラスの内部実装は確かにネイティブ リソースを含んでいますが、NetworkConnection 型はそれ自身のリソースの有効期間を管理するため、Database クラスからはマネージ リソースと見なされます。 しかし、NetworkConnection クラス内では、connectionHandle はコードで明示的に記述されていない限りクリーンアップされないため、ネイティブ リソースと見なされます。


CLR 徹底解剖 IDisposable について


MSDN マガジンに掲載された Shawn Farkas の記事です。ここでは定義に関する説明はないですが、実例を通して考察してます。興味深いのは、まず「アンマネージリソース」を「ネイティブ リソース」と呼んでいること。もうひとつが、ネイティブリソースを含むクラスは呼び出し側からは「マネージリソース」と見做すこと等の記述が見えます。ということは、IDispose インターフェイスを継承するクラスは、呼び出し側のクラスから見ると「マネージリソース」になるということか?だとすると次の記述と矛盾します。


Using ステートメント (Visual Basic)

マネージ リソースであれば、何もコードを追加しなくても、.NET Framework のガベージ コレクター (GC) が破棄してくれます。マネージ リソースに Using ブロックは必要ありません。


Using ステートメント (Visual Basic)


これを解釈すると、マネージリソースは Disopose 不要のオブジェクトとなる。っていうか、そもそもマネージリソースに Dispose があるのはおかしいということになるので、IDispose インターフェイスを継承するクラスはアンリソースマネージにならないか?


Dispose メソッドの実装

マネージ リソース (配列など) のみを使用する型は、ガベージ コレクターによって自動的にクリアされるため、このような型で Dispose メソッドを実装しても、パフォーマンス上の利点はありません。ネイティブ リソースを使用するマネージ オブジェクト、および .NET Framework に公開されている COM オブジェクトでは、主に Dispose メソッドを使用します。ネイティブ リソース (FileStream クラスなど) を使用するマネージ オブジェクトは、IDisposable インターフェイスを実装しています。


Dispose メソッドの実装


マネージリソースを「配列など」と言っている。・・・またここでもアンマネージリソースを「ネイティブ リソース」と呼んでますね。


他の人たちはどう言ってるか


ここで視点を変えて MS 以外の人達がどう認識してるか見てみます。

AQUA's .NET 奮闘記「リソースの解放」

2.Disposableなクラスは内部的にアンマネージリソースを持っており、この場合Usingなどでの解放が原則として望ましい。
  ※ 原則としたのは、補足的な情報として、基底クラスの都合で実際には、必要も無いのに IDisposable インターフェースを継承している場合もある為。しかし扱いは変えない。
3.メモリ(マネージリソース)の即時解放は参照型では存在しない。用途に合わせ値型にて設計するのも考慮の余地がある。


黒龍さんの発言の引用ですが、マネージリソースをメモリと解釈してます。

Yahoo! 知恵袋「Visual C#のリソースの解放について教えてください。」

マネージリソースというのは、クラスのオブジェクトが使用しているメモリのことです。それ以外の何物でもありません。アンマネージリソースというのは、ファイル、データベース接続、ウィンドウハンドルなど、マネージリソース以外のリソースのことです。


マネージリソースを「オブジェクトが使用しているメモリ、それ以外の何物でもない」と言ってます。

何となく Blog by Jitta 「Dispose、、、(その2)」

.NET Framework で扱うリソースには、大きく分けて2種類あります。.NET Framework アプリケーションの土台(つまり CLR)が管理する「マネージドリソース(GC ヒープ)」と、管理しない「アンマネージドリソース」です。IDisposable インターフェイスは、「アンマネージドリソースを使うことを、使用者に知らせる」ことを目的とします。そして、GC によって解放されるのは「マネージドリソース」です。言い方を変えると、CLR によって管理されているから「マネージドリソース」であって、CLR が管理しない「アンマネージドリソース」を管理するのは使用者(開発者)です。

マサのなにかできるかな「マネージリソースの開放」

マネージリソース(GC管理内:メモリ)とアンマネージリソース(GC管理外:ファイル)とか言われたりします。


上記お二人とも、CLR で管理するかしないかで、マネージリソースとアンマネージリソースを区別してますね。


The Root of .NET Framework


Microsoft で最もコアな話をする人、荒井さんが書いた本です。前に買ったけど紛失したため、今日ジュンク堂で買い直してきました。以前読んだときはあまりに難解な話題のため途中で挫折しましたが、この本ならリソースのことが詳しく書いてあるだろうと思って読み直してみたら、「Chapter4 メモリとプロセス」に詳しく書いてありました。以下、抜粋です。

4-1 CLR におけるメモリ管理


CLR におけるメモリ管理の仕組みは、「マネージ」あるいは「アンマネージ」と呼ばれます。これは、メモリをガベージコレクタが管理するか(Managed)、管理しないか(Unmanaged)という点で使い分けられてます。

メモリ管理 GC による回収 説明
マネージ あり 純粋に CLR の中だけで完結するリソース
アンマネージ なし OS リソースをラップしたリソース(ファイルハンドルやウィンドウハンドルなどをラップしたオブジェクトなど)、ユーザーが明示的に解放する必要があるもの


CLR が起動されると、CLR はプロセス用に連続したメモリ領域を予約します。この領域のことを「マネージヒープ」と呼びます。CLR と、CLR を呼び出したホストプロセスが確保するヒープは大別すると3種類になります。(図4-1)


図4-1 ヒープの種類



図4-1からは、表4-1の分類に属さない、ガベージコレクタによって回収されないマネージヒープが存在しているのがわかります。このマネージヒープは、Chapter3 で説明したアセンブリのロードによって構築されるメタデータ表が格納される領域のことです。


The Root of .NET Framework(P108)


「マネージリソース」を「純粋に CLR の中だけで完結するリソース」。「アンマネージ」を「OS リソースをラップしたリソースで、ユーザーが明示的に解放する必要があるもの」と明確に定義されています。さすがに日本で一番 .NET Framework の深部を知り尽くしている方の発言だけに、重さを感じます。
いまこの本を少し読み直してるんですが、以前より知識がだいぶ増えたせいか、前と違ってすんなり頭に入ってくるような気がします。なかなか低レベル(レベルが低いんじゃなくて、マシン語とかメモリとかその他諸々)な話題を扱っている本ですが、これは他にはないタイプの、非常に面白い本です。


The Root of .NET Framework

The Root of .NET Framework


結論

さて、どうやらこれで結論は見えてきたような気がします。

マネージリソース

純粋にCLR で管理されるリソース、マネージメモリとも呼ばれる。開発者はオブジェクトの解放を考慮する必要は全くない。

アンマネージリソース

外部リソース、共有リソース・ネイティブリソースとも呼ぶ。ファイルやハンドル、さらにそれをラップしているオブジェクトを指す。開発者はオブジェクトを明示的に解放する必要がある。


すると、最初の話はこうなると思います。

リソースという用語は例えば次のように使うべきと思われる。

double[],string等 マネージリソース
Stream,Bitmap等 アンマネージリソース
(外部の)ポインター アンマネージリソース

本当は Jeffrey・Richter の .NET Framework 第2版 とか Don・Box の Essential .NET とかも読みたかったのですが、なにぶん時間と金が足りません。しかも廃版になっている・・・

2010/09/07 追記。

しばし保留・・・結論を出すにはまだ早すぎた感が・・・

2010/09/10 追記

その後、The Root of .NET Framework の著者である荒井さんからコメントと詳細な解説を頂き、MSDN フォーラムでもさんざん大騒ぎした末に、こういう結論に至りました。

double[],string等 マネージリソース
Stream,Bitmap等 マネージリソース
(外部リソースの)ポインター マネージリソース
ポインターが指す外部リソース アンマネージリソース


ポインターもマネージリソースなんかい?と疑問に思う方もおられるでしょうが、荒井さんに言わせると

「アンマネージ・ポインタもマネージリソースの1つだということです。何故なら、ポインタ自体は符号なし整数であり、アンマネージリソースへのアドレスを保持しているに過ぎません。このポインタを介してアンマネージリソースを参照できるとしてもです」

とのことです。詳しくは以下の記事をご覧ください。

CLRから見たリソースについて

荒井さんにはお忙しい中、図まで作って解説していただいて本当に感謝いたします。この場を借りて御礼をさせて頂きます。<(_ _)>



プログラミングMS .NET FRAMEWORK 第2版 (マイクロソフト公式解説書)

プログラミングMS .NET FRAMEWORK 第2版 (マイクロソフト公式解説書)


Essential .NET ― 共通言語ランタイムの本質

Essential .NET ― 共通言語ランタイムの本質