Javaアプリケーションを開発・運用するにあたり、OSのメモリ管理の概要を知っておく必要があります。本節では、OSの一般的なメモリ管理技法の1つである仮想アドレス空間を説明します。
なお、仮想アドレス空間の具体的なアーキテクチャーはOSごとに異なりますが、本節では各OSに共通する内容を説明します。
仮想メモリ
OSは、物理メモリ(RAM)だけでなくスワップファイルを活用することにより、多くのメモリ領域を使用することができます。このテクノロジーを仮想メモリといいます。仮想メモリの容量は、RAMとスワップファイルのサイズの合計になります。
図7.1 OSが利用可能なメモリ容量
ただし、ハードディスクへのアクセスはRAMより低速なので、メモリのスワッピングは性能に大きく影響を与えますので、注意が必要になります。
仮想アドレス空間
OS上でプログラムを起動すると、OSがプログラムを実行・管理する単位としてのプロセスが生成されます。同様にして、Javaアプリケーションを起動すると、Javaプロセスが生成されます。
OS上で生成されたプロセスには、仮想アドレス空間が割り当てられます。仮想アドレス空間は、それぞれのプロセスで独立したものであり、あるプロセスから別のプロセスの仮想アドレス空間にアクセスすることはできません。マシンに積んでいる物理メモリ(RAM)のサイズとは関係なく、仮想アドレス空間のサイズは、常に一定です。
このため、大量の仮想メモリを用意しても、1つのプロセスで使用できるメモリ量の上限は仮想アドレス空間の上限になりますから、プロセスが仮想アドレス空間の大きさ(実際には後述のユーザ空間の大きさ)を超えるメモリ量を必要とする場合は、メモリ不足の状態になります。
逆に、プロセスが必要とするメモリ量が仮想アドレス空間の大きさ(実際には後述のユーザ空間の大きさ)の上限未満だったとしても、そのメモリ量に相当する仮想メモリ量がOS上になければ、メモリ不足の状態になります。
また、大量の仮想メモリを用意しても、OS上で大量のプロセスが動作していて仮想メモリを大量に消費している状態であれば、各プロセスに割り当てられた仮想アドレス空間を使い切っていなくても、メモリ不足の状態になる場合があります。
ユーザ空間
プロセスが持つ仮想アドレス空間のうち、実際にプロセスが使用できる空間を、ユーザ空間といいます。ユーザ空間には、プログラムの実体(Windows(R)でJavaアプリケーションを実行する場合は、java.exeなど)がコピーされるだけでなく、スタックやヒープなどのさまざまなセグメントがあります。更にユーザ空間は、実行するプログラムだけでなく、そのプログラムを実行させるためのOS側のプログラムなどでも使用します。Javaプロセスのユーザ空間の場合には、前述の各セグメントの他に、Javaオブジェクトを格納するセグメント(=Javaヒープ)があります。このため、Javaアプリケーションをチューニングする際の対象となるJavaヒープのサイズの上限値は、ユーザ空間のサイズよりも少なくなります。
Javaアプリケーションのチューニングを実施する際は、仮想メモリの容量やプロセスの使用状況など、システムの状態を考慮する必要があります。なおOSの種類やアプリケーションの実行モードによって、ユーザ空間として使用できる上限値が異なるため、注意が必要です。
なお、各セグメント獲得・解放時の実際の制御処理はOSが行います。そのため、各セグメントに関する管理方法/動作仕様/大きさについては、OpenJDKを実行する各OSの仕様に依存します。そして、プロセス外部からはユーザ空間に空きが存在するように見える場合であっても、OSの制御処理上は利用できる空きが無いと判断され、セグメントに対応するメモリ領域が獲得できず、プロセス動作が異常となる場合がありますので注意してください。
また、OSの制御処理上、各セグメント間には、アプリケーションから利用できない隙間領域が存在する場合があります。そのため、通常、ユーザ空間としての上限値まで仮想メモリを使用することはできません。プロセスが使用するメモリ量として、ユーザ空間の上限値まで使えることを前提とした設計にはしないでください。