Question

I have a program in which, partly for informational logging, I output the names of some classes as they are used (specifically I add an entry to a log saying along the lines of Messages::CSomeClass transmitted to 127.0.0.1). I do this with code similar to the following:

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

And yes, before anyone points it out, I realise that the output of typeinfo::name is implementation-specific.

According to MSDN

The type_info::name member function returns a const char* to a null-terminated string representing the human-readable name of the type. The memory pointed to is cached and should never be directly deallocated.

However, when I exit my program in the debugger, any "new" use of typeinfo::name() shows up as a memory leak. If I output the information for 2 classes, I get 2 memory leaks, and so on. This hints that the cached data is never being freed.

While this is not a major issue, it looks messy, and after a long debugging session it could easily hide genuine memory leaks.

I have looked around and found some useful information (one SO answer gives some interesting information about how typeinfo may be implemented), but I'm wondering if this memory should normally be freed by the system, or if there is something i can do to "not notice" the leaks when debugging.

I do have a back-up plan, which is to code the getMessageName method myself and not rely on typeinfo::name, but I'd like to know anyway if there's something I've missed.

Was it helpful?

Solution

Another solution is to correct the underlying problem. This is not really a memory leak, just a false report. The memory blocks allocated to the tyepinfo() and the name() string are assigned the wrong block type. It is probably not a good idea to "free" this memory, since an attempt will be made by the CRT to free it again. The good news is this was finally fixed in VS2012 (_MSC_VER 1700+).

Since this only applies to _DEBUG builds, the following may be a safer solution. The function _FixTypeInfoBlockUse() should be called as mentioned above just before exiting the module entry point (main, WinMain, etc.).

#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)

OTHER TIPS

I've just stumbled upon this issue trying to clean the log of VLD. Yes, this is a known bug, which is fixed in VC11 only. It exists in previous versions of MSVC including 2010. This bug appears only if you use MFC. If you use MFC as DLL instead of static library, the memory leak will still exist, but won't be detected.

There is a global cache of type_info names and it is not cleared (the excerpt from <typeinfo>):

struct __type_info_node {
    void *_MemPtr;
    __type_info_node* _Next;
};

extern __type_info_node __type_info_root_node;

The idea is to clear this cache. This function works for me:

#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;
   }
}

Call clear_type_info_cache() before exit. You can register it with atexit

#include <cstdlib>

int WinMain(...)
{
   atexit(&clear_type_info_cache);
   ...
}

or call it immediately before leaving 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;
   ...
}

As pointed out by Chris Parton in the comments, this appears to be a known bug, at least with the version of compiler I am using - upgrading to VC11 would correct the issue, if I were able to upgrade.

Attempting to delete the output of typeinfo::name() partially works:

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

However there are still some memory leaks - I just noticed that previously I appeared to be getting two leaks per call (perhaps due to the classes being inside a namespace?). Using the above version of code, this went down to one leak per call.

Another solution that appears to work is to link in the dynamic version of the MFC libraries (yes, I'm using MFC, don't judge me), rather than the static version.

VS stores type info in a singly-linked list. The header of this list is accessible by an opaque structure accessible by name __type_info_root_node. Actually it is a SLIST_HEADER structure.

Win32 API has a set of concurrency-safe function to work with such structures. To fix memory leaks report In your case you need to delete all nodes of this list.

#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;
}

Updated: VLD 2.5.1 does not report memory leaks on type_info::name() in VS2015 Update 3.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top