我有一个64位Visual Studio 2010(单线程)C ++ Windows应用程序,我正在尝试集成tcmalloc,并且在使用任何动态链接的dll时遇到问题。我将tcmalloc链接为静态库。 tcmalloc很有效,直到应用程序开始使用我们的共享dll之一。我将解决方案构建为64位DEBUG应用程序。所有dll都与CRT库的C / C ++调试版本(MSVCP100D.dll和MVCR100D.dll)相关联。
以下是失败的代码示例。为所有内存分配调用tcmalloc,但是当调用delete时,应用程序崩溃。我真的很困惑,因为当我在主可执行文件中创建一个函数并在那里复制代码时,完全相同的代码工作正常。
如果有人在这种情况下使用tcmalloc有任何经验,我将非常感谢您的反馈。这对我来说是一个谜。这是dll的内存模型问题(不同的堆??)?我不知道。在我看来,他们使用相同的堆。
很抱歉,如果这篇文章太长了。我试图提供尽可能多的信息。
谢谢。
布鲁斯
更新:作为测试,我将崩溃的共享dll更改为静态库,一切正常,直到应用程序使用不同的dll。因此无论出于何种原因,tcmalloc需要一些额外的步骤来处理共享的dll。我可以使用tcmalloc制作所有dll的静态库以进行内存分析,但知道使用共享还需要做什么才真的很好 dll与tcmalloc。
DLL头文件方法声明: __declspec(dllexport)static std :: string GetExecutablePath();
//。cpp implementation
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
}
答案 0 :(得分:1)
通过静态链接tcmalloc,每个使用它的DLL都会获得它自己的库内部状态副本(包括堆和所有指针)。如果您通过tcmalloc从一个DLL分配内存并尝试从另一个DLL中删除,则操作将失败,因为您最终访问了多个堆。
将tcmalloc链接为动态库,您的问题应该消失。
答案 1 :(得分:1)
我们终于让tcmalloc在带有共享dll的Windows 64位平台上运行。感谢Captain Oblivious的建议!诀窍是在Visual Studio 2010中使用本文中所述的调试符号构建发布版本:How to: Debug a Release Build。 tcmalloc与MFC中的CRT调试堆调用冲突,例如:_free_dbg。我们从tcmalloc分配内存并从MFC CRT堆调试调用中释放。在发布版本中,问题消失了。我们做了一些初步测试,tcmalloc正在生成堆配置文件调用图。
感谢。
布鲁斯