使用typeinfo :: name()后内存泄漏

时间:2011-11-29 09:47:00

标签: c++ visual-c++ memory-leaks typeid typeinfo

我有一个程序,其中,部分用于信息日志记录,我输出一些类的名称(特别是我在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*返回到以null结尾的字符串,表示该类型的可读名称。指向的内存是缓存的,不应该直接取消分配。

但是,当我在调试器中退出程序时,typeinfo::name()的任何“新”用法都会显示为内存泄漏。如果我输出2个类的信息,我会得到2个内存泄漏,依此类推。这暗示永远不会释放缓存的数据。

虽然这不是一个主要问题,但它看起来很混乱,经过长时间的调试后,它可以很容易地隐藏真正的内存泄漏。

我环顾四周并找到了一些有用的信息(一个SO答案提供了一些关于how typeinfo may be implemented的有趣信息),但我想知道这个记忆是否应该被系统释放,或者是否有什么东西我调试时可以做“不注意”泄漏。

我确实有一个备用计划,即自己编写getMessageName方法代码并且不依赖typeinfo::name,但无论如何我想知道是否有我错过的内容。

4 个答案:

答案 0 :(得分:4)

我刚刚在试图清除VLD的日志时偶然发现了这个问题。是的,这是known bug,仅在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;
   ...
}

答案 1 :(得分:4)

另一种解决方案是纠正潜在的问题。这不是真正的内存泄漏,只是一个错误的报告。分配给tyepinfo()和name()字符串的内存块被分配了错误的块类型。 “释放”这种记忆可能不是一个好主意,因为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)

答案 2 :(得分:1)

正如Chris Parton在评论中指出的那样,这似乎是known bug,至少对于我正在使用的编译器版本 - 升级到VC11会纠正问题,如果我能够升级的话。

尝试删除typeinfo::name()的输出部分有效:

std::string getMessageName(void) const
{
    std::string typeStr(typeid(*this).name());
    delete (typeid(*this).name());
    return typeStr;
}

然而,仍然存在一些内存泄漏 - 我只是注意到以前我似乎每次调用都会出现两次泄漏(可能是由于类在命名空间内?)。使用上面的代码版本,每个调用都会发生一次泄漏。

另一种似乎有用的解决方案是链接MFC库的动态版本(是的,我使用的是MFC,不要评判我),而不是静态版本。

答案 3 :(得分:0)

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 Update 3中type_info :: name()的内存泄漏。