none
VC2015での独自メモリ管理に関して RRS feed

  • 質問

  • VC2015にて new と delete をオーバーライドして独自のメモリ管理をしている方に質問です

    リリースビルドにてランタイムライブラリオプションをマルチスレッド (/MT)にしていて、かつcoutなどを利用している際に、

    アプリケーション起動直後に #pragma init_seg(compiler)  が記述されたコードからnewが呼ばれますが、

    そのアドレスのdeleteは、終了時は  #pragma init_seg(lib) のコード _Fac_tidy_reg_t のデストラクタから呼ばれてしまいます

    また、独自メモリ管理のクラスは他のグローバル変数なども管理内に含めるたいため、

     #pragma init_seg(lib) で定義したコードにて定義している関係で

    cout 関連のコードはメモリ管理が作成される前にnewが呼ばれますが、delete はメモリ管理クラスが破棄される前に 呼ばれてしまいます。

    結果deleteには管理外のアドレスが呼ばれることになりますが、管理外アドレスの場合はアサートを呼ぶようにしておりアサートが発生してしまいます。

    メモリ管理のクラスが作成される前に呼ばれたnewのアドレスを覚えておいて、

    管理外アドレスだった場合は覚えておいたアドレスと照らし合わせて、異なるアドレスだった場合は通常のdeleteを呼ぶようにするなど回避すればいいのですが、

    なかなかスマートではないとおもっています。

    なにかいい手をご存じの方はいらっしゃいますか?

    余談ですが、VS2015を利用する前は、VS2010を利用していたため、STLPortを使っていたためか、

    このようなことに悩んだことはありませんでした。(  #pragma init_seg(compiler) のコードから起動直後にnewされるようなことはなかった)

    2016年8月16日 17:53

回答

  • 起動時に大きいメモリを確保してもしなくてもプロセスで使用可能なメモリ量は変わらないため、newに失敗するかどうかも変わらないと思います…。逆にWindowsライブラリ(user32.dllとかdirectxとか)からすれば確保されたメモリは全て使用中と扱われるため、それらライブラリ側でメモリ確保に失敗しかねないです。またWindows VistaからはLow-fragmentation Heapが有効化されたため標準のメモリ管理でもメモリ確保に失敗しにくくなっています。ですのでデメリットの方が大きいように感じました。

    C++言語ではstd::allocatorが用意されており、std::vectorなどはこのallocatorを受け取るように設計されています。ですので、独自のメモリ管理を行いたいクラス・インスタンスに対してのみ独自に用意したallocator派生クラスを渡すことで管理できます。(mallocやWindowsライブラリを除いた)newだけのメモリ管理を行うよりも、管理したい個所だけに限定されてはどうでしょうか?

    2016年8月17日 22:58

すべての返信

  • 尚、ランタイムライブラリを /MD にすれば発生しません。

    2016年8月16日 17:54
  • 余談ですが、VS2015を利用する前は、VS2010を利用していたため、STLPortを使っていたためか、このようなことに悩んだことはありませんでした。(#pragma init_seg(compiler) のコードから起動直後にnewされるようなことはなかった)

    VS2015からC Runtime LibraryはC++言語で書き直されました。例えばprintf()も内部はC++言語での実装に更新されています。そのため、初期化ルーチン内でもoperator newされるようになりました。VS2010との違いはそのためでしょう。

    ですので、#pragma init_seg(compiler)内のコードからoperator newを呼ばれるのは避けようがありません。

    逆に質問ですが、独自のメモリ管理を行っている理由はなんでしょうか? 目的を達成するためにoperator newのオーバーライド以外の方法を模索した方がいいかもしれません。

    2016年8月17日 1:49
  • なるほどそうなんですね。。。

    独自メモリ管理を行っているのは、起動時に大きいメモリを確保してそのなかでやりくりすることで、

    アプリ動作途中で呼び出されるnewを失敗させないようにすることです。(ちなみにゲームアプリになります)

    その他、サイズや用途によって使用する領域を変えたりしています。

    グローバルのnewをオーバーライドするのではなく、クラス側にoperator new 等を用意していくべきなんでしょうかね、、

    ただそうすると利用しているライブラリ内から等変更できないコードから呼ばれるnewをどうするのかという問題が残りますが、、

    2016年8月17日 17:46
  • 起動時に大きいメモリを確保してもしなくてもプロセスで使用可能なメモリ量は変わらないため、newに失敗するかどうかも変わらないと思います…。逆にWindowsライブラリ(user32.dllとかdirectxとか)からすれば確保されたメモリは全て使用中と扱われるため、それらライブラリ側でメモリ確保に失敗しかねないです。またWindows VistaからはLow-fragmentation Heapが有効化されたため標準のメモリ管理でもメモリ確保に失敗しにくくなっています。ですのでデメリットの方が大きいように感じました。

    C++言語ではstd::allocatorが用意されており、std::vectorなどはこのallocatorを受け取るように設計されています。ですので、独自のメモリ管理を行いたいクラス・インスタンスに対してのみ独自に用意したallocator派生クラスを渡すことで管理できます。(mallocやWindowsライブラリを除いた)newだけのメモリ管理を行うよりも、管理したい個所だけに限定されてはどうでしょうか?

    2016年8月17日 22:58
  • ありがとうございます。限定的な管理を行う方向に直してみようかと思います。

    2016年8月19日 13:59