SQL Server のメモリ管理 - Part 1

神谷 雅紀
SQL Server Escalation Engineer

 

SQL Server のメモリ管理について、質問されることがよくあるため、今回から何回かに分けて、仮想アドレス空間 (Virtual Address Space) や物理メモリ (RAM) との関係も含めて、SQL Server のメモリ管理の概要をまとめます。この内容は、SQL Server 2000 ~ 2008 R2 までのバージョンにあてはまります。

 

仮想アドレス空間とは?

 

SQL Server に限らず、ユーザーモードアプリケーションのメモリ管理を理解しようとした場合、仮想アドレス空間について理解していることは必須です。しかし、多くの場合、物理メモリやページファイルとの区別が曖昧であったり、全く理解されていなかったりしています。そのため、まず最初に、仮想アドレス空間について説明します。もし、仮想アドレス空間や物理メモリ、ページファイルといった点は明確に理解しているということであれば、次回から読みはじめて下さい。

尚、仮想アドレス空間を意識しなければならないのは、ほとんどの場合 32-bit 環境であるため、以降は、32-bit 環境についての内容です。しかし、64-bit 環境と 32-bit 環境では、仮想アドレス空間のサイズは異なりますが、それ以外の概念は、基本的に同じであるため、ほとんどの内容は 64-bit にも当てはまります。

 

32-bit Windows 上で動作するすべてのユーザーモードアプリケーション (ユーザーモードプロセス) には、4GB の仮想的なアドレス空間 (アドレス 0x00000000 から 0xFFFFFFFF) が与えられます。これは、SQL Server であっても、Excel であっても、メモ帳 (notepad.exe) であっても、32-bit Windows 上で動作するユーザモードプロセスであれば、すべてのプロセスに対して同じサイズが与えられます。

また、物理メモリのサイズが 1GB であっても、8GB であっても、32GB であっても、物理メモリのサイズには全く関係なく、32-bit 環境では、仮想アドレス空間のサイズは常に 4GB です。

 

ユーザー仮想アドレス空間とカーネル仮想アドレス空間

 

仮想アドレス空間は、ユーザーモード用 (アプリケーション) とカーネルモード用 (システム) の領域に分割されます。下位 2GB がユーザーモード用、上位 2GB がカーネルモード用です (*1)

アプリケーションは、この仮想アドレス空間の中でメモリ割り当てを行います。新たなメモリ割り当ては、まだ使用されていない領域 (FREE とマークされている領域) からしか行うことができません。

また、仮想アドレス空間内で使用中の領域 (FREE ではない領域) は、アプリケーションが VirtualAlloc() Windows API などのメモリ割り当て API を呼び出して明示的に割り当てた領域だけではなく、EXE や DLL などの実行可能ファイルを置くために使用されている領域、各スレッドごとに割り当てられるスレッドスタックと呼ばれるスレッド固有のメモリ領域など、暗黙的に割り当てられている領域も含まれます。

以下は、Sysinternals のツール VMMAP により、32-bit 版 SQL Server 2008 の仮想アドレス空間の状態を見たものです。

 

 

グラフィカルではありませんが、これと同等の内容は、SQL Server の組み込み機能である Dinamic Management View (DMV / 動的管理ビュー) sys.dm_os_virtual_address_dump でも見ることができます。

(*1) /3GB や /USERVA BOOT.INI オプション等により、この割合を変更することもできます。64-bit Windows 上で 32-bit アプリケーションを動かした場合、アプリケーションは Windows On Windows (WoW64) 上で動作することになりますが、この場合、ユーザー仮想アドレス空間だけで 4GB のサイズがあります。

 

アプリケーションによるメモリ割り当てと仮想アドレス空間内の利用可能領域の関係

 

  • アプリケーションがあるサイズのメモリ割り当てを行おうとした時、利用可能、つまり、FREE とマークされている領域のうち、最大サイズの領域よりも割り当てようとしているサイズが大きければ、そのメモリ割り当ての試みは失敗します。例えば、VirtualAlloc() で 10MB を割り当てようとした時、仮想アドレス空間内で最も大きい FREE の領域が 10MB 未満であれば、この VirtualAlloc() 呼び出しは失敗します。
    アプリケーションでは、例えば、10MB のメモリが必要な場合に、1MB と 9MB というように、分断されたメモリ領域を合わせて使うことは、不可能ではないですが、プログラムでのメモリ管理が非常に複雑になるため、普通そのようなことは行いません。通常は、メモリ割り当てのためには、必要なサイズの連続した領域が必要です。

 

  • あるモジュール (DLL など) を新たにロードしようとした時に、FREE とマークされている領域のうち、最大サイズの領域よりも指定されたモジュールのファイルサイズが大きければ、そのモジュールのロードは失敗します。例えば、ファイルサイズが 500KB の DLL を LoadLibrary() でロードしようとした時に、仮想アドレス空間内で最も大きい FREE の領域が 500KB 未満であれば、この LoadLibrary() 呼び出しは失敗します。

 

  • 新たなスレッドを開始しようとした時に、FREE とマークされている領域のうち、最大サイズの領域よりもスレッドスタックのサイズの方が大きければ、スレッドの起動は失敗します。32-bit SQL Server 2000/2005/2008/2008 R2では、SQL Server ワーカースレッドのスレッドスタックサイズは 512KB です。従って、CreateThread() や beginthread() によりスレッドを開始しようとした時に、仮想アドレス空間内で最も大きい FREE の領域が 512KB 未満であれば、スレッド開始の試みは失敗に終わります。

仮想アドレス空間と物理メモリとの関係

 

仮想アドレス空間は、あくまでも「仮想」であるため、仮想アドレス空間でのメモリ割り当てがそのまま物理メモリ上の領域を使用する訳ではありません。

仮想アドレス空間内で割り当てられたメモリは、Windows のメモリマネージャによって、例えば、あるプロセスの固有のデータを含むアドレス 10000 からの 2KB は、物理メモリのアドレス 50000 からの 2KB に置かれているというように、物理メモリにマップされます。複数のプロセスが共有するファイルマッピングや共有するメモリ領域は、同じ物理メモリ領域が複数の仮想アドレス空間にマップされます。

また、Windows は、アプリケーションの実行に必要なデータは物理メモリ上におきますが、そうでないデータについては、必要に応じて、物理メモリから追い出します。いわゆるスワップアウト、ページングと呼ばれる操作です。プロセス仮想アドレス空間にマップされている物理メモリ上にある領域は、ワーキングセットと呼ばれるため、ページングにより物理メモリからデータが追い出される状況は、ワーキングセットトリム (working set trim、あえて訳すとすれば「作業セットの切り詰め」) と呼ばれることもあります。

SQL Server の仮想アドレス空間についても同じで、Windows は、必要に応じて物理メモリから SQL Server の仮想アドレス空間にマップされた領域を追い出すことができます。

 

ここまで、仮想アドレス空間について簡単に説明しましたが、次回以降は、SQL Server のメモリ管理について説明します。