使用typeInfo :: name()后内存泄漏
-
25-10-2019 - |
题
我有一个程序,部分是为了信息记录,我输出了某些类的名称(特别是我在日志中添加了一个条目, Messages::CSomeClass transmitted to 127.0.0.1
)。我以类似于以下的代码执行此操作:
std::string getMessageName(void) const {
return std::string(typeid(*this).name());
}
是的,在任何人指出之前,我意识到 typeinfo::name
是特定于实施的。
根据 MSDN
这
type_info::name
成员功能返回const char*
到代表该类型的人类可读名称的无效终止字符串。指向的记忆是缓存的,绝对不要直接划分。
但是,当我在调试器中退出程序时,任何“新”的使用 typeinfo::name()
显示为记忆泄漏。如果我输出两个类别的信息,我会得到2个内存泄漏,依此类推。这暗示了缓存的数据永远不会被释放。
尽管这不是一个主要问题,但看起来很混乱,经过长时间的调试会话,它很容易隐藏真正的内存泄漏。
我环顾四周,发现了一些有用的信息(一个答案提供了一些有关的有趣信息 如何实现TypeInfo),但是我想知道该内存是否通常应该被系统释放,或者我可以做一些事情来“不注意”调试时泄漏。
我确实有一个备份计划,那就是编码 getMessageName
我自己而不是依靠 typeinfo::name
, ,但是无论如何我想知道是否有我错过的东西。
解决方案
另一个解决方案是纠正基本问题。这并不是真正的内存泄漏,只是一个错误的报告。分配给tyepinfo()和名称()字符串的内存块分配了错误的块类型。 “释放”此记忆可能不是一个好主意,因为CRT将尝试再次释放它。好消息是,这最终在VS2012(_MSC_VER 1700+)中修复。
由于这仅适用于_debug构建,因此以下可能是更安全的解决方案。在退出模块入口点(Main,Winmain等)之前,应如上所述调用函数_fixtypeinfoblockuse()。
#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)
其他提示
我只是偶然发现了这个问题,试图清洁日志 vld. 。是的,这是一个 已知错误, ,仅在VC11中固定。它存在于MSVC的先前版本中,包括2010年。仅当您使用MFC时,才会出现此错误。如果将MFC用作DLL而不是静态库,则将仍然存在内存泄漏,但不会检测到。
有一个全局缓存的 type_info
名称且未清除(摘录 <typeinfo>
):
struct __type_info_node {
void *_MemPtr;
__type_info_node* _Next;
};
extern __type_info_node __type_info_root_node;
这个想法是清除此缓存。此功能对我有用:
#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;
}
}
称呼 clear_type_info_cache()
退出之前。您可以注册 atexit
#include <cstdlib>
int WinMain(...)
{
atexit(&clear_type_info_cache);
...
}
或在离开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;
...
}
正如克里斯·帕顿(Chris Parton)在评论中指出的那样,这似乎是 已知错误, ,至少使用我正在使用的编译器版本 - 如果我能够升级,将升级到VC11将纠正问题。
试图删除输出 typeinfo::name()
部分工作:
std::string getMessageName(void) const
{
std::string typeStr(typeid(*this).name());
delete (typeid(*this).name());
return typeStr;
}
但是,仍然有一些内存泄漏 - 我只是注意到以前我似乎每个通话都会泄漏两个泄漏(也许是由于类位置在命名空间内吗?)。使用以上版本的代码,每个呼叫的一个泄漏。
似乎有效的另一个解决方案是在MFC库的动态版本中链接(是的,我正在使用MFC,不要判断我),而不是静态版本。
VS存储在单连锁列表中键入信息。可以通过名称访问的不透明结构访问此列表的标题 __TYPE_INFO_ROOT_NODE. 。实际上,这是一个slist_header结构。
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;
}
更新:VLD 2.5.1在VS2015中的type_info :: name()上不报告内存泄漏3。