連載:明解.NETテクノロジ

アプリケーション・ドメイン
Application Domain

吉松 史彰
2003/10/22
Page1 Page2

概要

 アプリケーション・ドメインとは、共通言語ランタイム(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に代わる新しい実行環境では、すべてのメモリには対応する「型」が割り当てられており、互換性のない型同士を強制的に変換することはできない。これにより、メモリが破壊されるようなコードを実行することはできなくなった。

 

 INDEX
  明解.NETテクノロジ
  アプリケーション・ドメイン Application Domain
  1.従来のプロセスの成り立ち
    2.共通言語ランタイム版プロセス - アプリケーション・ドメイン
 


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間