none
スタックサイズが大きいとWindows7でCFileDialogが無応答になる RRS feed

  • 質問

  • スタックサイズが大きいとWindows7でCFileDialogが無応答になるという現象が発生して悩んでおります。

    WindowsXPで正常に動作していたアプリ(x86)をWindows7(x64)で動作させたところ
    ファイル選択ダイアログを開く処理(CFileDialogのDoModal)で無応答になりました。
    調査の結果、[プロジェクト]-[プロパティ]-[構成のプロパティ]-[リンカ]-[システム]-[スタックのサイズの設定]
    に1000000000と設定されているのが原因でした。

    ファイル選択ダイアログを表示するだけのテストアプリ(x86)を作成して確認したところ、
    スタックサイズが1000000000だとCFileDialogのDoModalで無応答になります。
    スタックサイズを62000000に減らすとCFileDialogのDoModalは正常に動作するのですが
    その直後にmalloc処理(150MBのメモリ確保)を行うと失敗するときがあります。
    スタックサイズを34000000以下にしてやれば安定して動作します。
    また、テストアプリをx64としてリビルドした場合はスタックサイズ1000000000でも
    正常に動作したのですが、x86で動作させたいです。

    // テスト関数
    void CtestDlg::OnBnClickedBtnTest()
    {
    	CFileDialog dlg( FALSE );
    	dlg.DoModal();
    
    	char *pTemp = (char *)malloc( 150000000 );
    	if( pTemp == NULL )
    	{
    		// メモリ確保エラー
    		return;
    	}
    	free( pTemp );
    }
    

    [テスト環境]
    OS:Windows7 Profesional(64bit)
    CPU:intel Core i7 860
    メモリ:4.0GB
    ※テストアプリはこの環境でVS2008 VC++で作成しました。

    Windows7で正常に動作させるにはスタックサイズを減らせばよいのですが、
    それだとアプリでスタックオーバーフローが発生してしまうため
    メモリを動的に確保するようにソースの修正を行わなければなりません。
    修正量が多くなるため、できればソースの修正はしたくないです。

    何かよい解決方法はありませんでしょうか?
    アドバイスよろしくお願いします。

    2012年4月4日 8:42

回答

  • malloc()150MBの直前のCFileDialogをコメントアウトすると
    malloc()で失敗しなくなりましたのでCFileDialogがメモリに何かしら影響を与えているということでしょうか?

    YESです。私以外の人も指摘している「スタックサイズを減らしましょう」はテストアプリではなく、スタックオーバーフローを引き起こしているアプリに対してでした。そのため、テストアプリに対して特に注意していませんでした。

    では、テストアプリがスタックを62MBにした上で、CFileDialogを実行後にmalloc()で150MBの確保に失敗する原因ですが、結論は変わりありません。純粋に使用可能なメモリー上限2GBに達したからです。
    何を浪費したかというと、CFileDialogを表示しているところでデバッガーでbreakしスレッドウィンドウを確認してみてください。私の環境では22threadsありました。62 * 22 = 1364MB ですのでこの段階でCFileDialogが大半のメモリを使用しています。その他、DLL等いろいろなデータにより残り150MBの空き容量が無かったのでしょう。

    ちなみに、Windows Vista以降、CFileDialogの内部実装がCOM化されており、Windows XP以前と比べて必要なスレッド数が増加しています。

    • 回答としてマーク mmiura 2012年4月5日 6:46
    2012年4月5日 6:22

すべての返信

  • [プロジェクト]-[プロパティ]-[構成のプロパティ]-[リンカ]-[システム]-[スタックのサイズの設定]
    に1000000000と設定されているのが原因でした。


    Windows7で正常に動作させるにはスタックサイズを減らせばよいのですが、
    それだとアプリでスタックオーバーフローが発生してしまうため
    メモリを動的に確保するようにソースの修正を行わなければなりません。

    1スレッド辺りのスタックサイズ1GBも要求するのは異常です。malloc()で150MBというのも異常です。根本的にC/C++言語を理解できていない、バグと言ってもいいでしょう。CFileDialogに関係なく、設計を見直すことをお勧めします。

    # 通常、1MBですので1024倍の値です。でしたらシステムメモリーも4GBでなく1000GBぐらい搭載しないとバランスが取れません。

    2012年4月4日 10:10
  • Windows7で正常に動作させるにはスタックサイズを減らせばよいのですが、
    それだとアプリでスタックオーバーフローが発生してしまうため
    メモリを動的に確保するようにソースの修正を行わなければなりません。
    修正量が多くなるため、できればソースの修正はしたくないです。

    あきらめてやり直すべきだと、私も思います。
    解決策を求められているようですが、存在しない答えを求めていても始まりませんので、できる体制・期間を作っていってください。(できる体制・期間=大規模改良して十分な検証ができる体制、日程)

    今まで たまたま 動いていたに過ぎないプログラムをほったらかしてきたツケが出ただけです。
    小さい体制、短い納期のちょっとした仕様変更・修正だったのかもしれませんが、現況をきちんと説明し、できる体制を作ってください。
    それができなければ、あきらめる方向に倒すことになるでしょう。プログラムの機能を削る、32bit バージョンを提供しないなど。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年4月4日 13:44
    モデレータ
  • 基本的に上のお二人と同意見ですが、「どうにかできないか」を考えてみるテスト。

    32ビットのアプリケーションを、Wow64 で動作させているのでしょうか?32ビットの Windows 7 だと、どうでしょうか。

    64ビットでコンパイルすれば動くということなので、Wow64 を使うことで、何かまずいことが発生していると思われます。ということは、Wow64 を使わないようにすることが解決方法だと思います。


    Jitta@わんくま同盟

    2012年4月4日 13:58
  • 64ビットでコンパイルすれば動くということなので、Wow64 を使うことで、何かまずいことが発生していると思われます。ということは、Wow64 を使わないようにすることが解決方法だと思います。

    64bit プロセスだと使えるメモリ空間がかなり広がるので、メモリ確保失敗と言うことがなくなるから、「64bit でコンパイルすると動く」のでは?
    32bit としてビルドしつつ、64bit 環境でも動かしたいという要求も明示されていないだけで存在しそうなので、WOW64 から離れることも難しいかと思っていますが…。

    # 使用量(コミット?)が 1GB 超えてくるような状況だとメモリ確保に失敗することが多いと思います。
    # なので、そこまでいくような設計は環境によっては動かないことも覚悟すべし。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年4月4日 14:05
    モデレータ
  • 回答ありがとうございます。

    スタックサイズ1GBが異常なのは分かりましたが、malloc()で150MBというのは異常なのでしょうか?

    質問欄に記載している「スタックサイズ62MBのときmalloc()の150MBに失敗する」という理由も

    よく分かりません。

    メモリに関する基本的なことが理解できておらず大変恐縮です。

    ご教授お願いできませんでしょうか?

    2012年4月5日 2:03
  • 回答ありがとうございます。
    皆様のアドバイスを見ているとやはり設計を見直すしかないようですね。
    2012年4月5日 2:06
  • 回答ありがとうございます。
    32bit版のWindows7でも正常に動作はしませんでした。
    64bit版のWindows7でも動作させたいです。
    2012年4月5日 2:10
  • # 使用量(コミット?)が 1GB 超えてくるような状況だとメモリ確保に失敗することが多いと思います。

    # なので、そこまでいくような設計は環境によっては動かないことも覚悟すべし。

    了解しました。
    スタックサイズというのはデフォルトから変更しないほうがよいのでしょうか?
    どのくらいまでなら問題ないのかがよく分かっていません。


    2012年4月5日 2:14
  • 32bitアプリケーションではアプリケーションが使用できるメモリーは2GBまでです。32bit = 4GBですが、残り2GBはOSが使用します。
    2GBのうちスタックサイズに1GB使用してしまうとmalloc()に使用できるメモリーはおよそ1GBとなります。この状況で150MB確保を行うと6回で使い果たしてしまいエラーになります。
    スタックサイズを62MBにしても、malloc() 150MBは13回ぐらいで2GBを使い果たします。

    このようにmalloc()が失敗する理由は単純にメモリー上限に達したからです。ですので、スタックサイズ1GBも異常ですし、malloc()で無計画に150MBを確保するのも異常です。

    スタックサイズはデフォルトのままにすべきです。スタックの構造を理解していて意図してスタックメモリーを使用している場合にのみ変更すべきです。今回のように62MB~1GBなどという値になっているのは、スタックの構造を理解していないからです。

    • (勝手な予想ですが)C/C++言語でどのようなコードを書けばスタックを使用するのか把握していないからではないでしょうか? 場合によっては無意味にデータをスタックにコピーすること(本来はコピー不要)でプログラムの動作が遅くなっていることも予想されます。
    • alloca()を使うと容易にスタックメモリーを浪費できますが、普通にmalloc()を使うべきです。
    2012年4月5日 2:31
  • スタックサイズを62MBにしても、malloc() 150MBは13回ぐらいで2GBを使い果たします。

    このようにmalloc()が失敗する理由は単純にメモリー上限に達したからです。ですので、スタックサイズ1GBも異常ですし、malloc()で無計画に150MBを確保するのも異常です。

    質問欄のテスト関数を見てもらえれば分かると思いますが、
    malloc() 150MBは1回しか行っていません。
    成功した場合はすぐに開放しますので、メモリを使いはたすということは無いと思うのですが。
    2012年4月5日 3:15
  • 本当に質問通りならそもそもスタック不足にはなり得ません。説明が省かれている部分に、スタックやヒープ(malloc()で使用する領域)を浪費する処理が含まれているはずです。
    その結果、残り150MBを切っていれば、malloc()は1回目であろうと失敗します。

    「malloc() 150MBは1回しか行っていません。」という書き方をする時点で、どこで浪費されているのかご自身で把握できていないんですよね?

    2012年4月5日 3:44
  • 本当に質問通りならそもそもスタック不足にはなり得ません。説明が省かれている部分に、スタックやヒープ(malloc()で使用する領域)を浪費する処理が含まれているはずです。
    その結果、残り150MBを切っていれば、malloc()は1回目であろうと失敗します。

    「malloc() 150MBは1回しか行っていません。」という書き方をする時点で、どこで浪費されているのかご自身で把握できていないんですよね?

    説明不足でした。すいません。
    テストアプリというのはダイアログベースのプロジェクトを新規作成し、
    画面にはTestというボタンを追加したのみです。(スタックサイズは62MB)
    そしてTestボタンをクリックしたときの処理が記載したテスト関数になります。

    無駄な処理は一切ありませんので
    スタックサイズの62MBとmalloc()150MB以外で大量にスタックやヒープを浪費することは考えられません。

    malloc()150MBの直前のCFileDialogをコメントアウトすると
    malloc()で失敗しなくなりましたのでCFileDialogがメモリに何かしら影響を与えているということでしょうか?


    • 編集済み mmiura 2012年4月5日 5:45
    2012年4月5日 5:30
  • malloc()150MBの直前のCFileDialogをコメントアウトすると
    malloc()で失敗しなくなりましたのでCFileDialogがメモリに何かしら影響を与えているということでしょうか?

    YESです。私以外の人も指摘している「スタックサイズを減らしましょう」はテストアプリではなく、スタックオーバーフローを引き起こしているアプリに対してでした。そのため、テストアプリに対して特に注意していませんでした。

    では、テストアプリがスタックを62MBにした上で、CFileDialogを実行後にmalloc()で150MBの確保に失敗する原因ですが、結論は変わりありません。純粋に使用可能なメモリー上限2GBに達したからです。
    何を浪費したかというと、CFileDialogを表示しているところでデバッガーでbreakしスレッドウィンドウを確認してみてください。私の環境では22threadsありました。62 * 22 = 1364MB ですのでこの段階でCFileDialogが大半のメモリを使用しています。その他、DLL等いろいろなデータにより残り150MBの空き容量が無かったのでしょう。

    ちなみに、Windows Vista以降、CFileDialogの内部実装がCOM化されており、Windows XP以前と比べて必要なスレッド数が増加しています。

    • 回答としてマーク mmiura 2012年4月5日 6:46
    2012年4月5日 6:22
  • 何を浪費したかというと、CFileDialogを表示しているところでデバッガーでbreakしスレッドウィンドウを確認してみてください。私の環境では22threadsありました。62 * 22 = 1364MB ですのでこの段階でCFileDialogが大半のメモリを使用しています。その他、DLL等いろいろなデータにより残り150MBの空き容量が無かったのでしょう。

    ちなみに、Windows Vista以降、CFileDialogの内部実装がCOM化されており、Windows XP以前と比べて必要なスレッド数が増加しています。

    非常に分かりやすい説明で納得できました。
    CFileDialogの何が問題なのかよく分かっていませんでしたので、これでスッキリしました。
    スタックサイズを減らして設計を見直します。
    本当にありがとうございました。

    2012年4月5日 6:46
  • 問題は、

    1. アプリケーションでスタックオーバーフローが発生した
    2. スタックサイズを増加させるとオーバーフローしなくなった
    3. 代わりにCFileDialogが不安定になった
    4. スタックサイズを減少させるとCFileDialogが動作するようになった

    という依存関係だと思います。

    3.の原因は先の返信の通り、CFileDialogが使用するスレッド数が以前よりも増加したためです。回答を求められていたのは3.だったのかもしれませんが、これは根本の問題1.と関係がありません。ですので1.ついてのみ返信していました。説明が回りくどくなってすみませんでした。

    2012年4月5日 8:20