Domanda

Ho un programma in cui, in parte per la registrazione di informazione, ho in uscita i nomi di alcune classi in cui sono utilizzati (in particolare ho aggiungere una voce a un registro dicendo lungo le linee di Messages::CSomeClass transmitted to 127.0.0.1). Lo faccio con un codice simile al seguente:

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

E sì, prima che qualcuno lo fa notare, mi rendo conto che l'uscita del typeinfo::name è l'attuazione-specifica.

MSDN

La funzione membro type_info::name restituisce un const char* ad una stringa con terminazione null che rappresenta il nome leggibile del tipo. La memoria indicò viene memorizzata nella cache e non dovrebbe mai essere deallocato direttamente.

Tuttavia, quando esco il mio programma nel debugger, qualsiasi uso "nuovo" di spettacoli typeinfo::name() come una perdita di memoria. Se l'uscita ho le informazioni per 2 classi, ottengo 2 perdite di memoria, e così via. Questo suggerimenti che i dati memorizzati nella cache non viene mai essere liberati.

Anche se questo non è un grosso problema, sembra disordinato, e dopo una lunga sessione di debug potrebbe facilmente nascondere le perdite di memoria genuine.

Ho guardato intorno e ho trovato alcune informazioni utili (una sola risposta SO fornisce alcune interessanti informazioni come typeinfo possono essere realizzate ), ma mi chiedo se questa memoria dovrebbe di norma essere liberata dal sistema, o se c'è qualcosa che posso fare per "non comunicazione" le perdite in fase di debug.

ho un piano di riserva, che è quello di codice il metodo getMessageName me stesso e non fare affidamento su typeinfo::name, ma mi piacerebbe sapere in ogni caso se c'è qualcosa che ho perso.

È stato utile?

Soluzione

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)

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top