64bit Windowsを前提とした32bitアプリケーション延命法 ~ LAAオプションで32bitアプリケーションのメモリ不足問題を解消

こんにちは。ウェブテクノロジの清水です。

Windowsアプリケーション開発者向けに、ちょっとしたTipsを紹介します。今回はVisual Studioの「LAAオプション」(LARGEADDRESSAWAREオプション、4GBオプション)という機能です。かなり便利な機能なのですが、知らない人も多いようです。

64bit化したくてもできない事情

オンメモリでデータを処理するタイプの32bitのWindowsアプリケーションでは、メモリ不足が結構深刻な問題となることがあります。

アプリケーションを64bit化すれば解決すると分かっていても、動作対象OSを64bit版Windowsのみに限定するのはユーザにとっても営業面から見ても抵抗がありそうです。

Windows XPのサポート終了が迫っている最近では、アプリケーションの動作対象OSからWindows XPを外すことも、ようやく普通の仕様として認められるようになってきていると思いますが、まだまだ無視できないのが32bit版のWindows 7のユーザです。

また、

  • 使用している社外ライブラリに64bit版が用意されていない
  • ソフト規模が大きかったり、使用しているライブラリが多すぎて、工数的に全部64bit対応するのが困難
  • インラインアセンブラを使っている部分があるので64bit化できない(64bitコンパイラがインラインアセンブラに未対応のため)
  • 配布パッケージやアップデート差分などを毎回32bit版と64bit版の2セット用意したくない

など、様々な理由でアプリケーションの32bit・64bit両対応を諦め、引き続き32bit版のみでアプリケーションの開発を続けている場合も多いのではないでしょうか?

2GBの壁

Windowsの場合、PCに4GB以上のメモリを搭載していても、通常、1個の32bitアプリケーションで利用可能なメモリ容量は約2GBです。

32bitアプリケーションがnewやmalloc()やGlobalAlloc()などで確保できるヒープメモリの容量は、この2GBからプログラム本体や各種のDLLが占有するメモリエリアの容量が引かれるため、さらに少なくなります。

実際に簡単なプログラムを作って調べてみると、確保できるヒープメモリの総容量は約1.9GBでした(環境や一度に確保するデータサイズによって多少変わります)。

これでは、数百MBの巨大なデータをオンメモリで複数同時に扱うようなタイプのアプリケーションでは、すぐにメモリ確保に失敗してしまいます。

テストに使用したプログラム

▲クリックで拡大
テストに使用したプログラム

通常ビルド時の実行結果

通常ビルド時の実行結果

弊社のアプリケーションでは、使用しているDLLが多いためか、アプリケーションから利用可能なヒープメモリ容量は1.5GB程度になりました。

実際、「メモリを16GB搭載していて、空きメモリは十分にあるのに、どうしてメモリ不足エラーが表示されるのか?」という問い合わせが弊社ユーザーサポート宛に届くこともありました。

アプリケーションによっては、独自に仮想記憶のような処理を行って、32bitアプリケーションで2GBを超える大量のデータを扱えるようにしているものもありますが、そのような処理を全く想定せずに開発されたアプリケーションでは、後から独自の仮想記憶のような処理を追加するのは難しいものです。

「LAAオプション」でヒープ倍増

実は、Visual Studioで開発している32bitアプリケーションでは、「LAAオプション」(LARGEADDRESSAWAREオプション)というものを使用して、64bit版のWindowsで実行した場合に、32bitアプリケーションが利用可能なメモリ容量を4GB弱まで増やす方法があるのです。

32bit版のWindowsのユーザもまだまだ残っているとはいえ、すでにユーザの多くは64bit版のWindowsを使っています。

このようなユーザにとっては、32bitアプリケーションでも「LAAオプション」が設定されていれば、64bit版Windowsの大容量メモリの恩恵(の一部)にあずかることができるわけです。

32bitアプリケーションで「LAAオプション」を使用するのは簡単で、たとえば、Visual Studio 2012の場合、開発中のアプリケーション(EXE)のプロジェクトのプロパティを表示し、

構成プロパティ → リンカー → システム → 大きいサイズのアドレス

の設定を「はい (/LARGEADDRESSAWARE)」に変更してビルドするだけで「LAAオプション」が有効になります。

「LAAオプション」でヒープ倍増

▲画像クリックで拡大
Visual Studio 2008などでは「2GBを超えるアドレスをサポートする(/LARGEADDRESSAWARE)」という表現になっています。

この変更を行ってビルドされた32bitアプリケーションは、従来の32bit Windows上で実行した場合は従来と同様に2GB弱の制限のままですが、64bit版のWindows上で実行すると4GB弱のメモリが使用できるようになります。

簡単なプログラムを作って試してみると、この「LAAオプション」を有効にすることで、確保できるヒープメモリは約3.9GBになりました。「LAAオプション」を設定しない場合と比較して、使えるヒープメモリがほぼ倍増したことになります。

LAAオプション有効時の実行結果

LAAオプション有効時の実行結果

なお、「LAAオプション」の設定自体はEXE作成用のプロジェクトだけでなく、DLL作成用のプロジェクトにも存在しますが、実際に有効なのはEXE作成用のプロジェクトの方だけです。

DLL側のコードでヒープメモリの確保を行っているような構造のプログラムでも、EXE側のプロジェクトの「LAAオプション」の設定が優先され、DLL側のプロジェクトの「LAAオプション」の設定は無視されます。

弊社のアプリケーションで「LAAオプション」を有効にしたところ、利用可能なヒープメモリ容量が3.6GB程度まで増えました。お客様から「メモリ不足エラーになる」という問い合わせをいただくこともなくなりました。

4GBパッチ

Visual Studioの「LAAオプション」はコンパイラではなくリンカーのオプションで、実際はビルドされたEXEファイルのヘッダ部分に「LAAオプション」の識別フラグが設定されるだけです。

WindowsのOS側が、このフラグを見て、その32bitアプリケーションに対するメモリ管理を2GBにするか4GBにするかを自動的に判別しているのです。

従って、たとえば、既存の32bitアプリケーションのEXEファイルをバイナリ編集して、この「LAAオプション」フラグを追加することも可能です。

このバイナリ編集は、既存の32bitアプリケーションのメモリ不足に悩んでいるユーザの一部では「4GBパッチ」などと呼ばれているようです。

もちろん、ユーザによるEXEファイルのバイナリ編集は、改ざんチェックを行っているプログラムでは使えませんし、電子署名付きのEXEファイルの場合は署名が壊れてしまいますので、アプリケーション開発者が自らアプリケーションのプロジェクトのプロパティで設定を変更し、正規にプログラムをビルドしなおすのが正しい「LAAオプション」対応です。

32bit Windowsでも「/3GB」起動スイッチ付き環境で効果あり

実は、Windows XPなどの32bit版のWindowsでも、「boot.ini」で「/3GB」起動スイッチを有効にしていると、「LAAオプション」を有効にしたアプリケーションが利用できるメモリの上限が2GBから3GBに拡張されます。

(※「/3GB」起動スイッチの詳細についてはマイクロソフト社のサポートページ等をご覧ください。)

実際に「/3GB」起動スイッチ付きの32bit Windows環境を用意して簡単な32bitプログラムを作って試してみると、約2.8GBのヒープメモリが使用できるようになりました。1つの32bitアプリケーションでヒープメモリとして使用できるメモリが約1GB増えたことになります。

「boot.ini」の編集は非常にハイリスクなので(書き換えを失敗するとWindowsが起動しなくなる可能性がある)、一般のWindowsユーザに「boot.ini」の設定変更を勧めるのは不適切ですが、すでに自主的に「/3GB」起動スイッチを有効にしてる上級ユーザなどにとっては、「LAAオプション」を有効にした32bitアプリケーションが増えることは歓迎されるでしょう。

「LAAオプション」の注意点

こんな便利な「LAAオプション」ですが、ちょっとだけ注意点があります。
「LAAオプション」を有効にしたプログラムが利用しているすべてのコード(DLLなども含む)の中で、確保したヒープメモリに対して不適切な処理を行っている箇所があると、プログラムが誤動作してしまいます。

不適切な処理の例

  • 確保可能なヒープメモリの総容量が2GB以下であることを前提にしている
  • ヒープメモリのアドレスが0x7fffffff以下であることを前提にしている

おそらく普通のプログラムでは、このような不適切な処理を行っている箇所はまずないと思いますが、意図的、あるいは意図せずに、このような不適切な処理を行ってしまっている場合は、「LAAオプション」を付けることでアプリケーションが正常に動作しなくなる可能性があります。

「LAAオプション」を付けた場合だけ不具合が発生する、という問題が発生したら、その時は不適切な処理を行っている場所を突き止めてプログラムを修正する必要があります。
もし社外ライブラリなどソース変更が不可能な部分でこの問題が発生したら、その時は残念ながら「LAAオプション」を諦めるしかありません。

弊社の場合では、今のところ、「LAAオプション」を有効にしてリリースしている「OPTPiX imesta7」(C++ネイティブ)、および、「コミPo!」(C++/CLI)で、「LAAオプション」が原因と思われる問題は発生していません。

以上、Windows用の32bitアプリケーションを開発されている方々の参考になれば幸いです。

タグ , , | 2020/06/16 更新 |