8

我有一个程序,在该程序中,部分用于信息日志记录,我在使用某些类时输出它们的名称(特别是我在日志中添加了一个条目,如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成员函数返回一个以 null 结尾的字符串,表示该const char*类型的人类可读名称。指向的内存是缓存的,永远不应该直接释放。

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

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

我环顾四周,发现了一些有用的信息(一个 SO 答案提供了一些关于如何实现 typeinfo 的有趣信息),但我想知道这个内存是否应该由系统正常释放,或者是否有什么我可以做的调试时“不注意”泄漏。

我确实有一个后备计划,即getMessageName自己编写方法而不是依赖于typeinfo::name,但无论如何我想知道是否有我错过的东西。

4

4 回答 4

4

我刚刚偶然发现了这个问题,试图清理VLD的日志。是的,这是一个已知错误,仅在 VC11 中修复。它存在于包括 2010 在内的 MSVC 的早期版本中。仅当您使用 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;
   ...
}
于 2012-06-23T21:01:56.573 回答
4

另一种解决方案是纠正根本问题。这并不是真正的内存泄漏,只是虚假报告。分配给 tyepinfo() 和 name() 字符串的内存块被分配了错误的块类型。“释放”此内存可能不是一个好主意,因为 CRT 会尝试再次释放它。好消息是这最终在 VS2012 (_MSC_VER 1700+) 中得到修复。

由于这仅适用于 _DEBUG 构建,因此以下可能是更安全的解决方案。函数 _FixTypeInfoBlockUse() 应该在退出模块入口点(main、WinMain 等)之前调用,如上所述。

#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)
于 2013-04-06T00:46:45.440 回答
1

正如 Chris Parton 在评论中指出的那样,这似乎是一个已知的错误,至少对于我正在使用的编译器版本 - 如果我能够升级,升级到 VC11 将纠正这个问题。

尝试删除typeinfo::name()部分工作的输出:

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

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

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

于 2011-11-29T11:24:21.873 回答
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() 的内存泄漏。

于 2017-09-06T11:41:00.083 回答