Frage

Ich habe ein Programm, in dem ich teilweise zur Informationsprotokollierung die Namen einiger Klassen ausgibt, sobald sie verwendet werden Messages::CSomeClass transmitted to 127.0.0.1). Ich mache dies mit Code ähnlich wie folgt:

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

Und ja, bevor jemand darauf hinweist, merke ich, dass die Ausgabe von typeinfo::name ist implementierungsspezifisch.

Entsprechend Msdn

Das type_info::name Die Mitgliedsfunktion gibt a zurück const char* zu einer null-terminierten Zeichenfolge, die den menschlich-lesbaren Namen des Typs darstellt. Der Gedächtnis, auf das hingewiesen wird, wird zwischengespeichert und sollte niemals direkt behandelt werden.

Wenn ich jedoch mein Programm im Debugger beende, jede "neue" Verwendung von "Neue" typeinfo::name() zeigt sich als Speicherleck. Wenn ich die Informationen für 2 Klassen ausgibt, erhalte ich 2 Speicherlecks und so weiter. Dies deutet darauf hin, dass die zwischengespeicherten Daten niemals befreit werden.

Dies ist zwar kein großes Problem, aber es sieht unordentlich aus und könnte nach einer langen Debugging -Sitzung echte Speicherlecks leicht verbergen.

Ich habe mich umgesehen und einige nützliche Informationen gefunden (eine Antwort, also gibt ich einige interessante Informationen darüber Wie Typinfo implementiert werden kann), aber ich frage mich, ob dieses Gedächtnis normalerweise vom System befreit werden sollte oder ob ich etwas tun kann, um die Lecks beim Debuggen nicht zu bemerken.

Ich habe einen Backup-Plan, der das Codieren hat getMessageName Methode selbst und nicht auf verlassen typeinfo::name, Aber ich würde es trotzdem gerne wissen, ob ich etwas verpasst habe.

War es hilfreich?

Lösung

Eine andere Lösung besteht darin, das zugrunde liegende Problem zu beheben. Dies ist nicht wirklich ein Speicherleck, nur ein falscher Bericht. Die Speicherblöcke, die dem Tyepinfo () und der Zeichenfolge () () () zugewiesen wurden, werden den falschen Blocktyp zugewiesen. Es ist wahrscheinlich keine gute Idee, diese Erinnerung zu "frei", da die CRT versucht wird, sie erneut zu befreien. Die gute Nachricht ist, dass dies schließlich in VS2012 (_msc_ver 1700+) behoben wurde.

Da dies nur für _debug -Builds gilt, kann Folgendes eine sicherere Lösung sein. Die Funktion _fixtypeinfoblockuse () sollte wie oben erwähnt aufgerufen werden, bevor Sie den Moduleinstiegspunkt (Haupt, WinMain usw.) verlassen.

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

Andere Tipps

Ich bin gerade auf dieses Problem gestoßen, um das Protokoll von zu reinigen Vld. Ja, das ist ein Bekannter Fehler, der nur in VC11 fixiert ist. Es existiert in früheren Versionen von MSVC, einschließlich 2010. Dieser Fehler wird nur angezeigt, wenn Sie MFC verwenden. Wenn Sie MFC als DLL anstelle einer statischen Bibliothek verwenden, gibt es weiterhin das Speicherleck, wird jedoch nicht erkannt.

Es gibt einen globalen Cache von type_info Namen und es wird nicht gelöscht (der Auszug von <typeinfo>):

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

extern __type_info_node __type_info_root_node;

Die Idee ist, diesen Cache zu löschen. Diese Funktion funktioniert für mich:

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

Anruf clear_type_info_cache() Vor dem Ausgang. Sie können es registrieren atexit

#include <cstdlib>

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

oder rufen Sie es unmittelbar vor dem Verlassen von Winmain an

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

Wie Chris Parton in den Kommentaren hervorgeht, scheint dies ein Bekannter Fehler, Zumindest mit der Version von Compiler, die ich verwende, würde das Upgrade auf VC11 das Problem korrigieren, wenn ich in der Lage wäre, ein Upgrade zu erzielen.

Versuch, die Ausgabe von zu löschen typeinfo::name() teilweise funktioniert:

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

Es gibt jedoch immer noch einige Speicherlecks - ich habe gerade bemerkt, dass ich zuvor zwei Lecks pro Anruf zu bekommen schien (vielleicht aufgrund der Klassen in einem Namespace?). Mit der obigen Codeversion ging dies auf ein Leck pro Anruf zurück.

Eine andere Lösung, die zu funktionieren scheint, besteht darin, die dynamische Version der MFC -Bibliotheken zu verknüpfen (ja, ich verwende MFC, beurteilen Sie mich nicht) und nicht in der statischen Version.

VS Stores Typen Infos in einer einzig verbundenen Liste. Der Header dieser Liste ist durch eine undurchsichtige Struktur zugänglich, die mit Namen zugegriffen wird __TYPE_INFO_ROOT_NODE. Eigentlich ist es eine Slist_Header -Struktur.

Die Win32-API verfügt über eine Reihe von gleichzeitbedingten Funktionen, die mit solchen Strukturen arbeiten können. Um Speicherlecks in Ihrem Fall zu beheben, müssen Sie alle Knoten dieser Liste löschen.

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

Aktualisiert: VLD 2.5.1 meldet keine Speicherlecks auf type_info :: name () in vs2015 Update 3.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top