C++ 使用typeinfo::name()后内存泄漏
我有一个程序,其中部分用于信息日志记录,我在使用某些类时输出它们的名称(具体地说,我在日志中添加了一个条目,按照传输到127.0.0.1的C++ 使用typeinfo::name()后内存泄漏,c++,visual-c++,memory-leaks,typeid,typeinfo,C++,Visual C++,Memory Leaks,Typeid,Typeinfo,我有一个程序,其中部分用于信息日志记录,我在使用某些类时输出它们的名称(具体地说,我在日志中添加了一个条目,按照传输到127.0.0.1的消息::CSomeClass的顺序)。我使用与以下类似的代码执行此操作: std::string getMessageName(void) const { return std::string(typeid(*this).name()); } 是的,在有人指出之前,我意识到typeinfo::name的输出是特定于实现的 据 type_info::n
消息::CSomeClass的顺序)。我使用与以下类似的代码执行此操作:
std::string getMessageName(void) const {
return std::string(typeid(*this).name());
}
是的,在有人指出之前,我意识到typeinfo::name
的输出是特定于实现的
据
type_info::name
成员函数将const char*
返回到表示该类型的人类可读名称的以null结尾的字符串。指向的内存是缓存的,不应直接释放
但是,当我在调试器中退出我的程序时,typeinfo::name()
的任何“新”用法都会显示为内存泄漏。如果我输出两个类的信息,我会得到两个内存泄漏,依此类推。这暗示缓存的数据永远不会被释放
虽然这不是一个大问题,但它看起来很混乱,在长时间的调试会话之后,它很容易隐藏真正的内存泄漏
我环顾四周,发现了一些有用的信息(其中一个答案提供了一些有趣的信息),但我想知道这个内存是否应该由系统正常释放,或者我是否可以在调试时“不注意”泄漏
我确实有一个备份计划,那就是自己编写getMessageName
方法,而不是依赖typeinfo::name
,但我想知道我是否遗漏了什么。正如Chris Parton在评论中指出的,这似乎是一个,至少对于我正在使用的编译器版本,如果我能够升级,升级到VC11可以纠正这个问题
尝试删除typeinfo::name()
的输出部分有效:
std::string getMessageName(void) const
{
std::string typeStr(typeid(*this).name());
delete (typeid(*this).name());
return typeStr;
}
但是仍然存在一些内存泄漏——我只是注意到,以前我似乎每次调用都会出现两次泄漏(可能是因为类位于名称空间内?)。使用上述版本的代码,每次调用都会出现一次泄漏
另一个似乎有效的解决方案是链接MFC库的动态版本(是的,我使用的是MFC,不要评判我),而不是静态版本。我刚刚偶然发现这个问题,试图清理的日志。是的,这是a,仅在VC11中固定。它存在于MSVC的早期版本中,包括2010年。此错误仅在使用MFC时出现。如果使用MFC作为DLL而不是静态库,则内存泄漏仍然存在,但不会被检测到
有一个名为的全局缓存,未被清除(摘自
):
我们的想法是清除这个缓存。此功能适用于我:
#include <typeinfo>
void clear_type_info_cache()
{
__type_info_node* & node = __type_info_root_node._Next;
while(node)
{
if (node->_MemPtr)
{
delete node->_MemPtr;
}
__type_info_node* tempNode = node;
node = node->_Next;
delete tempNode;
}
}
或者在离开WinMain之前马上给它打电话
struct dummy_scope_exit
{
typedef void (*Fun)();
dummy_scope_exit(Fun f) : m_f(f) {}
~dummy_scope_exit() { m_f(); }
Fun m_f;
};
int WinMain(...)
{
dummy_scope_exit cleaner = &clear_type_info_cache;
...
}
另一个解决方案是纠正根本问题。这不是真正的内存泄漏,只是一个错误的报告。分配给TyeInfo()和name()字符串的内存块分配了错误的块类型。“释放”这个内存可能不是一个好主意,因为CRT会尝试再次释放它。好消息是,这最终在VS2012中得到了修复(_MSC_VER 1700+)
由于这只适用于_debugbuilds,以下可能是更安全的解决方案。在退出模块入口点(main、WinMain等)之前,应如上所述调用函数_FixTypeInfoBlockUse()
#如果定义(_DEBUG)&&(_MSC_VER>=1000&&u MSC_VER\u Next;
(((((CrtMemBlockHeader*)pNode)-1->nBlockUse=\U CRT\U块;
如果(pNode->_MemPtr!=NULL)
(((((_CrtMemBlockHeader*)pNode->_MemPtr)-1->nBlockUse=_CRT_BLOCK;
pNode=pNext;
}
}
#endif//defined(_DEBUG)&&(_MSC_VER>=1000&&&u MSC_VERVS将类型信息存储在一个单独链接的列表中。此列表的标题可以通过一个不透明结构访问,该结构可以通过名称\u type_info\u root\u节点访问。实际上它是一个SLIST\u标题结构
Win32 API有一组并发安全函数来处理此类结构。
若要修复案例中的内存泄漏报告,您需要删除此列表中的所有节点
#include <Windows.h>
#include <typeinfo>
#include <vld.h>
void ClearTypeinfoCache()
{
#ifdef _DEBUG
while (auto entry = InterlockedPopEntrySList(reinterpret_cast<PSLIST_HEADER>(&__type_info_root_node)))
{
free(entry);
}
#endif
}
int main()
{
atexit(ClearTypeinfoCache);
return 0;
}
#包括
#包括
#包括
void ClearTypeinfoCache()
{
#ifdef_调试
while(自动输入=InterlocatedPopEntrySlist(重新解释转换(&&uuuuu类型\uu信息\uu根节点)))
{
免费(入境);
}
#恩迪夫
}
int main()
{
atexit(ClearTypeinfoCache);
返回0;
}
更新:VLD2.5.1未报告类型_info::name()上的内存泄漏在VS2015更新3中。可能相关?您使用的是什么编译器?如果可能的话,可以尝试其他编译器?因为它是缓存的,所以不要担心它。@jagansai:我不担心泄漏本身,因为它只影响应用程序退出时的调试器输出-我担心它可能隐藏真正的内存泄漏。而且它看起来很混乱。我喜欢整洁的调试器输出:)@ChrisParton:谢谢你的链接-在谷歌搜索时没有发现这一点(我认为我是一个优秀的谷歌搜索者)。我使用的是VC++2008,它有“bug”,目前无法移动到任何更新的版本。我注意到有一个解决方法(手动删除typeinfo实例?),我会很快试用。@icabod:不用担心,让我知道它是怎么回事。在没有其他人的情况下接受我自己的答案。是的,你只删除了一个字符串的内存,因此会出现一次泄漏。还有相应的要删除的单链表节点。请参阅我的答案。这是一个非常糟糕的解决方案,因为字符串缓存在内存中,因此如果你使用c所有typeid(*this).name()在删除之后,您将再次得到一个垃圾字符串。将我接受的答案更改为该字符串,因为它比其他答案更干净(?),部分原因是#if
将其限制为仅受影响的版本。
struct dummy_scope_exit
{
typedef void (*Fun)();
dummy_scope_exit(Fun f) : m_f(f) {}
~dummy_scope_exit() { m_f(); }
Fun m_f;
};
int WinMain(...)
{
dummy_scope_exit cleaner = &clear_type_info_cache;
...
}
#if defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699)
//
// Debug memory block header:
// o Borrowed from the Microsoft CRT to fix the false "memory leak" report
// when using typeinfo 'name' accessor in a _DEBUG build of the library.
//
struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
#ifdef _WIN64
int nBlockUse;
size_t nDataSize;
#else
size_t nDataSize;
int nBlockUse;
#endif
long lRequest;
unsigned char gap[4];
};
static void __cdecl _FixTypeInfoBlockUse(void)
{
__type_info_node* pNode = __type_info_root_node._Next;
while(pNode != NULL)
{
__type_info_node* pNext = pNode->_Next;
(((_CrtMemBlockHeader*)pNode) - 1)->nBlockUse = _CRT_BLOCK;
if (pNode->_MemPtr != NULL)
(((_CrtMemBlockHeader*)pNode->_MemPtr) - 1)->nBlockUse = _CRT_BLOCK;
pNode = pNext;
}
}
#endif//defined(_DEBUG) && (_MSC_VER >= 1000 && _MSC_VER <= 1699)
#include <Windows.h>
#include <typeinfo>
#include <vld.h>
void ClearTypeinfoCache()
{
#ifdef _DEBUG
while (auto entry = InterlockedPopEntrySList(reinterpret_cast<PSLIST_HEADER>(&__type_info_root_node)))
{
free(entry);
}
#endif
}
int main()
{
atexit(ClearTypeinfoCache);
return 0;
}