ホームに戻る
 10、メモリ管理

メモリはページという単位で管理される。
例えば32ビット系の場合ページ番号を20ビット、
各ページのオフセットに12ビットで割り振っている。
12ビットではページのサイズが4KB(=4096Bytes)となる。
12ビットサイズのページが20ビット個なので、
最大で4GBまでのメモリにアクセスできることになる。

具体的なページサイズは GetsystemInfo で
SYSTEM_INFO 構造体のポインタを指定すると、
SYSTEM_INFO.dwPageSize にページサイズを入れて返ってくる。

1つのアプリケーションはこの4GBのメモリを持つことができ、
特に何もしなければ他のアプリケーションと重複しない。
見た目上はこの範囲でメモリを自由に使うことができる。
ただし各アプリケーションに4GBの実メモリを割り振るには、
現状では実メモリのサイズが大きく不足している。
ここで Windows はページングという方法を用いている。
ページングでは仮想的にメモリが4GBあると仮定し、
もしその一部を使う場合があればページ単位で実メモリを割り当て、
使用頻度が少ないものから切り離してハードディスクなどに退避する方法である。
つまり仮想メモリとして4GBのアドレスは確保されるが、
実際に存在するメモリは実メモリのサイズに制限される。

仮想メモリの状態には3つある。
「コミット」と「予約」と「フリー」の3つ。
コミットは仮想メモリに実メモリが割り振られている状態。
予約はコミットする予定があり、まだ実メモリが割り振られていない領域。
連続したアドレスを利用する場合にその長さを予約できる。
フリーは実メモリも割り振られず自由にできる領域。

0x00000000 コミット
0x00001000 コミット
0x00002000 予約
0x00003000 予約
0x00004000 予約
0x00005000 コミット
0x00006000 コミット
0x00007000 フリー

例えば上の例では1つめと2つめのページがコミットされている。
このコミットされたページは仮想的に連続した領域として利用できる。
ただし実メモリでこのページが繋がっているとはいえない。
もし領域を拡張したい場合は3つめの予約領域をコミットする。
コミットされると3つぶんの連続した領域が利用できるようになる。
4、5つめのページも予約されているため、
このまま最大5ページまで連続した仮想メモリで領域を確保できる。
その先は既に他の用途でコミットされているため拡張できない。
もし拡張する可能性があればその十分な量を先に予約しておくべきである。
必要量をコミットし、十分量を予約するのがセオリー。

動的に割り振られる領域には3つある。
「スタック」と「ディフォルト・ヒープ」と「ヒープ」の3つ。
スタックはプログラムが自動で利用する領域。
スタックは上位のアドレスから下位のアドレスへと利用される。
初期コミットサイズと予約領域サイズはコンパイル時に指定できる。
スタックに関しては一度コミットされたページは
再び予約領域に戻されることが無い。
ただ長期使われないとページアウトの対象になるようなので、
無駄にメモリが使われるというようなこともないようだ。
ディフォルト・ヒープはAPI等も利用する領域。
システム側も利用するため、プログラムで使用する場合は、
利用する領域がフリーであるかを調べる必要がある。
読み書きの属性が指定でき、違反するとアクセス違反が発生する。
初期コミットサイズと予約サイズはコンパイル時に指定。
ヒープはプログラム側でしか利用できないと保証されている。
よってプログラム側で管理がしやすい領域といえる。
ヒープの確保はまず初期コミットサイズと予約領域を指定し、
確保ができるとヒープのハンドルを得ることができる。
次にこのヒープのハンドルから利用するサイズを指定する。
この時のサイズはページ単位でなくとも良い。
得られた領域をブロックと呼び、先頭ポインタで管理する。
ヒープは複数確保することができ、
ヒープのハンドルと先頭ブロックのポインタで区別する。
一般にフラグメンテーションの回避のため、
ヒープは確保した逆順に解放していくことが望まれる。

 ディフォルト・ヒープ

LPVOID VirtualAlloc(
  LPVOID lpAddress,        // 領域の先頭アドレス
  SIZE_T dwSize,           // 領域のサイズ
  DWORD flAllocationType,  // 割り当てのタイプ
  DWORD flProtect          // アクセス保護のタイプ
);

lpAddress:NULL で自動的に確保できるアドレスを指定。
dwSize:領域のサイズに該当するページを指す。
flAllocationType:MEM_COMMIT or MEM_RESERVE。
flProtect:領域の属性
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
PAGE_NOACCESS

戻り値は、
成功時に先頭アドレス、失敗時は NULL。

BOOL VirtualFree(
  LPVOID lpAddress,   // 領域のアドレス
  SIZE_T dwSize,      // 領域のサイズ
  DWORD dwFreeType    // 操作タイプ
);

dwFreeType:MEM_DECOMMIT or MEM_RELEASE。

戻り値は、
成功時 0 以外の数値、失敗時は 0。

 ヒープ

HANDLE HeapCreate(
  DWORD flOptions,       // ヒープ割り当て方法の属性
  SIZE_T dwInitialSize,  // 初期コミットサイズ
  SIZE_T dwMaximumSize   // 初期予約サイズ
);

flOptions
HEAP_GENERATE_EXCEPTIONS:メモリ不足時に NULL を返さずシステムに例外を渡す。
HEAP_NO_SERIALIZE:ヒープへの相互排他を行わない。

戻り値は、
成功時はヒープのハンドル、失敗時は NULL。

BOOL HeapDestroy(
  HANDLE hHeap   // ヒープのハンドル
);

戻り値は、
成功時は 0 以外の数値、失敗時は 0。

LPVOID HeapAlloc(
  HANDLE hHeap,   // プライベートヒープブロックのハンドル
  DWORD dwFlags,  // ヒープの割り当て方法の制御
  SIZE_T dwBytes  // 割り当てたいバイト数
);

dwFlags:
HEAP_GENERATE_EXCEPTIONS:メモリ不足時に NULL を返さずシステムに例外を渡す。
HEAP_NO_SERIALIZE:ヒープへの相互排他を行わない。
HEAP_ZERO_MEMORY:ヒープを 0 で初期化。

戻り値は、
成功時は先頭ポインタ、失敗時は NULL。

BOOL HeapFree(
  HANDLE hHeap,  // ヒープのハンドル
  DWORD dwFlags, // ヒープ解放オプション
  LPVOID lpMem   // メモリへのポインタ
);

dwFlags:
HEAP_NO_SERIALIZE:ヒープへの相互排他を行わない。

戻り値は、
成功時は 0 以外の数値、失敗時は 0。

inserted by FC2 system