5

tcmalloc を統合しようとしている 64 ビットの Visual Studio 2010 (シングル スレッド) C++ Windows アプリケーションがあり、動的にリンクされた dll のいずれかを使用すると問題が発生します。tcmalloc をスタティック ライブラリとしてリンクしました。tcmalloc は、アプリケーションが共有 dll の 1 つを使用し始めるまではうまく機能します。ソリューションを 64 ビット DEBUG アプリケーションとして構築しました。CRT ライブラリの C/C++ デバッグ バージョン (MSVCP100D.dll および MVCR100D.dll) とのすべての dll のリンク。

以下は、失敗するコードの例です。すべてのメモリ割り当てに対して tcmalloc が呼び出されますが、delete が呼び出されると、アプリケーションがクラッシュします。メインの実行可能ファイルで関数を作成し、そこにコードをコピーすると、まったく同じコードが正常に機能するため、本当に不可解です。

このような状況で tcmalloc を使用した経験のある方がいらっしゃいましたら、フィードバックをいただければ幸いです。それは私には謎です。dllの(異なるヒープ??)のメモリモデルの問題ですか?知らない。彼らは同じヒープを使用しているようです。

この投稿が長すぎる場合は申し訳ありません。できるだけ多くの情報を提供しようとしました。

ありがとうございました。

ブルース

更新: テストとして、クラッシュしていた共有 dll を静的ライブラリに変更し、アプリケーションが別の dll を使用するまで、すべて正常に動作しました。そのため、何らかの理由で tcmalloc が共有 dll を処理するためにいくつかの追加手順が必要になります。tcmalloc を使用してメモリ プロファイリング用の dll の静的ライブラリをすべて作成できますが、共有 dll を tcmalloc で使用するには他に何をする必要があるかを知っておくとよいでしょう。

DLL ヘッダー ファイルのメソッド宣言: __declspec(dllexport) static std::string GetExecutablePath();

//.cpp の実装

string Parameters::GetExecutablePath()

    string  execPathStr;
    char exeFilePath[ MAX_PATH +1];
    if ( GetModuleFileName( NULL, exeFilePath, MAX_PATH ) )
    {
        //The line of code below is where the app crashes.
        //It calls operator new in crt/src/new.cpp. I verified the call to malloc
        //is forwarded to tcmalloc. 
        *execPathStr = string(exeFilePath);* //creates and deletes a temporary and then crashes

        long dir_pos = execPathStr.rfind( FT_DIR_SLASH ) ;
        execPathStr = execPathStr.substr( 0, dir_pos+1 );
    }

    return execPathStr;

}

一時文字列が破棄されたときに呼び出されるメソッド:

~_String_val()
{   
    // destroy the object
    typename _Alloc::template rebind<_Container_proxy>::other _Alproxy(_Alval);
    this->_Orphan_all();
    _Dest_val(_Alproxy, this->_Myproxy);
    **_Alproxy.deallocate(this->_Myproxy, 1);**
    this->_Myproxy = 0;
}


void deallocate(pointer _Ptr, size_type)
{   
    // deallocate object at _Ptr, ignore size
    **::operator delete(_Ptr);**
}

This is where it crashes. the pHead->nBlockUse is 0. 
crt/dbgdel.cpp:

void operator delete(
        void *pUserData
        )
{
    //code omitted for brevity
    /* verify block type */
    **_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));** //crashes here
}

tcmalloc を共有 DLL として再構築した後、メモリを解放しようとすると別の場所でクラッシュするようになりました。

afxmem.cpp:

void __cdecl operator delete(void* p)
{
#if !defined(_AFX_NO_DEBUG_CRT) && defined(_DEBUG)
        **_free_dbg(p, _NORMAL_BLOCK);** <-------- this function gets called 
#else
        free(p);
#endif
}

dbgheap.c:

extern "C" _CRTIMP void __cdecl _free_dbg(
        void * pUserData,
        int nBlockUse
        )
{
 _mlock(_HEAP_LOCK);

        __try {
            /* allocate the block
             */
            **_free_dbg_nolock(pUserData, nBlockUse);**
        }
   __finally {
            /* unlock the heap
             */
            _munlock(_HEAP_LOCK);
        }
}



extern "C" void __cdecl _free_dbg_nolock(
        void * pUserData,
        int nBlockUse
        )
{
    //code omitted for brevity

    /*
    * If this ASSERT fails, a bad pointer has been passed in. It may be
    * totally bogus, or it may have been allocated from another heap.
    * The pointer MUST come from the 'local' heap.
    */
    **_ASSERTE(_CrtIsValidHeapPointer(pUserData));** <-------- crashes here
}
4

2 に答える 2

2

最終的に、tcmalloc が共有 dll を使用する Windows 64 ビット プラットフォームで動作するようになりました。Captain Oblivious の提案に感謝します。この記事で説明されているように、デバッグ シンボルを使用して Visual Studio 2010 でリリース バージョンをビルドするのが秘訣でした: How to: Debug a Release Build。tcmalloc は、MFC の CRT デバッグ ヒープ呼び出し (_free_dbg など) と競合します。tcmalloc からメモリを割り当て、MFC CRT ヒープ デバッグ呼び出しから割り当てを解除していました。リリースビルドでは、その問題はなくなりました。いくつかの予備テストを行ったところ、tcmalloc がヒープ プロファイル コール グラフを生成しています。

ありがとう。

ブルース

于 2013-01-16T23:26:09.847 に答える
1

tcmalloc を静的にリンクすることにより、それを使用する各 DLL は、ライブラリの内部状態 (ヒープとすべてのポインターを含む) の独自のコピーを取得します。ある DLL から tcmalloc を介してメモリを割り当て、別の DLL から削除しようとすると、複数のヒープにアクセスすることになるため、操作は失敗します。

tcmalloc を動的ライブラリとしてリンクすると、問題は解消されます。

于 2013-01-09T18:33:49.287 に答える