 |
|
連載:明解.NETテクノロジ
アプリケーション・ドメイン
Application Domain
吉松 史彰
2003/10/22 |
|
概要
アプリケーション・ドメインとは、共通言語ランタイム(CLR)における、実行コードの管理単位である。従来のWindowsにおけるプロセスに相当する。アプリケーション・ドメインの機能は、AppDomainクラス(System名前空間)を介して外部に公開されている。
従来のプロセスの成り立ち
Windows上でアプリケーションが実行されると、対応する「プロセス」が作成される。Windowsのプロセスは、メモリ領域、セキュリティ・トークン、ファイルやシリアル・ポートなどの各種リソースなどの管理単位となる。Windowsのプロセスの内部には1つ以上のスレッドが配置され、コードが実行される。同じプロセス内部のスレッドで動作しているコードは、メモリ空間やファイルなどのリソースを共有することになる。
Windows 3.1では、メモリ領域はWindows全体で管理されており、あるアプリケーションが設定したメモリ上の値を、ほかのアプリケーションが参照できるようになっていた。そのため、アプリケーションが誤動作を起こすと、そのアプリケーションだけではなくほかのアプリケーションまで巻き込んでしまったり、最悪の場合はWindowsシステムまでもが巻き込まれてダウンしてしまったりすることがしばしばあった。
 |
Windows 3.1におけるメモリ領域の管理 |
メモリ領域はWindows全体で管理されていたため、アプリケーションの誤動作がほかのアプリケーションや、最悪の場合Windowsシステムまでも巻き込んでしまうことがしばしばあった。 |
Windows NT以降では、堅牢性を高めるために、アプリケーションが個別のプロセスとして起動されるようになった。各アプリケーションにはそれぞれ専用のメモリ空間が割り当てられ、ほかのアプリケーションのメモリにアクセスしたり、OSのメモリにアクセスしたりすることはできなくなった。
 |
Windows NTにおけるメモリ領域の管理 |
アプリケーションは個別のプロセスとして起動され、それぞれ専用のメモリ空間が割り当てられる。ほかのアプリケーションやWindowsシステム自体のメモリ領域にはアクセスできない。 |
これによって堅牢性は向上したが、OSは大きな負荷を背負い込むことになった。一般に、1つのOS上で動作しているプロセスの数が増えれば増えるほど、OS自体の管理作業が増大し、動作が重くなってしまう。プロセスを分けることで堅牢性は高まったが、パフォーマンスが犠牲になってしまったのである。
共通言語ランタイムにおける型の安全性
共通言語ランタイム(CLR)では、堅牢性をよりいっそう高めるために、型の安全性(タイプ・セーフティ)という概念を導入している。この概念は、次のようなC++のコードで説明できる。
#include <iostream>
using namespace std;
class Data {
public:
short x;
short y;
};
class Data2 {
public:
long z;
};
void main() {
Data *d = new Data();
d->x = 100;
d->y = 200;
cout << d->x << ", " << d->y << endl; // 出力 :100, 200
Data2 *d2 = (Data2*)d; // <-問題のコード
if (d2)
d2->z = 9876543;
cout << d->x << ", " << d->y << endl; // 出力:-19393, 150
delete d;
d = NULL;
}
// コンパイル方法:cl /GX badcode.cpp
|
|
型の安全性を示すためのサンプル・プログラム(アンマネージ・コード) |
C++で記述されたこのコードからは、従来形式のネイティブ・コード(アンマネージ・コード)ができあがる。この形式では、互換性がない型へ強制的にキャストを行っていてもプログラムは実行できる。なお、.NET Framework SDKがインストールされていればこのコードはコンパイル可能。 |
上記のコードは、コンパイルでき実行も可能であるが、結果としてDataクラスのインスタンスのデータを破壊する。「問題のコード」部分で、本来互換性のないキャストが行われてしまうためである。
 |
互換性のないキャストにより破壊されるデータ |
従来のアンマネージ・コードでは、2つのshort型(16ビット)の値をlong型(32ビット)としてアクセスするといったコードが記述できてしまう。この場合、もともと代入されていた2つの値は破壊され、意味のない値となってしまう。 |
上記のコードを共通言語ランタイム上で動作するように次のように書き換えてみよう。次のコードは(warningが出力されるものの)コンパイルは可能だ。しかし実行すると、問題のコード部分でSystem.InvalidCastException例外が発生する。
#include <iostream>
using namespace std;
// 追加
#using <mscorlib.dll>
// __gc を追加
__gc class Data {
public:
short x;
short y;
};
// __gc を追加
__gc class Data2 {
public:
long z;
};
void main() {
// __gc を追加
Data __gc *d = new Data();
d->x = 100;
d->y = 200;
cout << d->x << ", " << d->y << endl;
// __gc を追加
Data2 __gc *d2 = (Data2*)d; // <-問題のコード
if (d2)
d2->z = 9876543;
cout << d->x << ", " << d->y << endl;
}
// コンパイル方法:cl /clr /GX managedbadcode.cpp
|
|
型の安全性を示すためのサンプル・プログラム(マネージ・コード) |
オプション“/clr”を付けてコンパイルすると共通言語ランタイムで動作可能なマネージ・コードとしてコンパイルできる。共通言語ランタイム上では互換性のない型同士を強制的に変換するようなコードを実行することはできない。 |
従来のC++で記述されたコード(アンマネージ・コード)では、Windowsは「d」が「d2」に代入されてしまうのを拒否できない。Windowsにとってポインタとは決められたサイズの数値であるだけで、それが実際に何を指しているのかを検出して実行を拒否することは不可能だからだ。しかし、共通言語ランタイムという、Windowsに代わる新しい実行環境では、すべてのメモリには対応する「型」が割り当てられており、互換性のない型同士を強制的に変換することはできない。これにより、メモリが破壊されるようなコードを実行することはできなくなった。
Insider.NET 記事ランキング
本日
月間