2011-11-29 13 views
8

Mam program, w którym, częściowo do rejestrowania informacji, wypisuję nazwy niektórych klas, ponieważ są one używane (konkretnie dodaję wpis do dziennika mówiącego wzdłuż linii Messages::CSomeClass transmitted to 127.0.0.1). Czynię to z kodu podobnego do poniższego:Wycieki pamięci po użyciu typeinfo :: name()

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

I tak, zanim ktokolwiek zwraca go, zdaję sobie sprawę, że wyjście typeinfo::name zależy od konkretnej implementacji.

Według MSDN

type_info::name funkcji członka zwraca const char* do nul ciąg reprezentujący postaci czytelnej dla człowieka nazwę typu. Wspomniana pamięć jest przechowywana w pamięci podręcznej i nigdy nie powinna być deallocated.

Jednak po wyjściu z programu w debugerze, każde "nowe" użycie typeinfo::name() pojawia się jako wyciek pamięci. Jeśli wysyłam informacje dla 2 klas, otrzymuję 2 wycieki pamięci i tak dalej. Wskazuje to, że dane w pamięci podręcznej nigdy nie są zwalniane.

Chociaż nie jest to poważny problem, wygląda na brudny, a po długiej sesji debugowania może z łatwością ukryć rzeczywiste wycieki pamięci.

Rozejrzałem się i znalazłem kilka przydatnych informacji (jedna odpowiedź SO daje interesujące informacje o how typeinfo may be implemented), ale zastanawiam się, czy pamięć ta powinna być normalnie uwolniona przez system, czy też jest coś, co mogę zrobić, aby "nie zauważam" nieszczelności podczas debugowania.

Mam plan awaryjny, który polega na kodowaniu metody getMessageName i nie poleganiu na typeinfo::name, ale chciałbym wiedzieć, czy jest coś, co przeoczyłem.

+1

Ewentualnie istotne? [http://connect.microsoft.com/VisualStudio/feedback/details/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name](http://connect.microsoft.com/VisualStudio/ feedback/details/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name). Z jakiego kompilatora korzystasz? Może wypróbujesz inny kompilator, jeśli to możliwe? –

+0

Ponieważ jest buforowany, nie martw się o to. – Jagannath

+0

@jagansai: Nie martwię się o sam wyciek, ponieważ wpływa on tylko na wyjście debuggera na wyjściu aplikacji - martwię się, że może ukryć * prawdziwe * wycieki pamięci. I wygląda na brudny. Naprawdę lubię czysty wydruk debuggera :) – icabod

Odpowiedz

4

Innym rozwiązaniem jest korekta podstawowy problem. To naprawdę nie jest wyciek pamięci, tylko fałszywy raport. Blokom pamięci przypisanym do ciągu tyepinfo() i nazwy() przypisywany jest niewłaściwy typ bloku. Prawdopodobnie nie jest dobrym pomysłem "uwolnienie" tej pamięci, ponieważ CRT podejmie próbę ponownego uwolnienia. Dobra wiadomość jest ostatecznie rozwiązana w VS2012 (_MSC_VER 1700+).

Ponieważ dotyczy to tylko wersji _DEBUG, poniższe rozwiązanie może być bezpieczniejszym rozwiązaniem. Funkcja _FixTypeInfoBlockUse() powinna zostać wywołana, jak wspomniano powyżej, tuż przed wyjściem z punktu wejścia modułu (główne, WinMain itp.).

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

Zmieniono moją zaakceptowaną odpowiedź na to pytanie, ponieważ jest trochę czystsza (?) Niż inne odpowiedzi, częściowo z powodu "# if" ograniczania go tylko do komplikacji, których dotyczy problem. – icabod

1

Jak zauważył Chris Parton w komentarzach, wygląda na to, że jest to known bug, przynajmniej z wersją używanego kompilatora - uaktualnienie do wersji VC11 naprawiłoby problem, gdybym mógł dokonać aktualizacji.

Próba usunięcia wyjściu typeinfo::name() częściowo działa:

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

Jednak nadal istnieją pewne wycieki pamięci - Właśnie zauważyłem, że wcześniej I wydawało się być coraz dwa przecieki na wezwanie (może ze względu na zajęcia bycia wewnątrz przestrzeni nazw?). Korzystając z powyższej wersji kodu, spadło to do jednego przecieku na połączenie.

Innym rozwiązaniem, które wydaje się działać, jest połączenie w dynamicznej wersji bibliotek MFC (tak, używam MFC, nie osądzaj mnie), zamiast wersji statycznej.

+0

Przyjmowanie własnej odpowiedzi pod nieobecność innych. – icabod

+0

Tak, otrzymujesz jeden wyciek podczas usuwania pamięci tylko dla ciągu znaków. Istnieje również odpowiadający pojedynczy połączony węzeł listy do usunięcia. Zobacz moją odpowiedź. – Stas

+0

To naprawdę złe rozwiązanie, ponieważ ciąg jest zapisywany w pamięci podręcznej, więc jeśli wywołasz funkcję typeid (* this) .name() ponownie po usunięciu, otrzymasz ciąg śmieci. –

4

Właśnie natknąłem się na ten problem, próbując wyczyścić dziennik VLD. Tak, jest to known bug, który jest ustalony tylko w VC11. Istnieje w poprzednich wersjach MSVC, w tym 2010. Ten błąd pojawia się tylko wtedy, gdy używasz MFC. Jeśli używasz MFC jako biblioteki DLL zamiast biblioteki statycznej, przeciek pamięci nadal będzie istniał, ale nie zostanie wykryty.

Jest to globalny cache type_info nazw i nie jest czyszczony (odpisu z <typeinfo>):

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

extern __type_info_node __type_info_root_node; 

Chodzi o to, aby usunąć ten cache. Ta funkcja działa dla mnie:

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

połączenia clear_type_info_cache() przed wyjściem. Można zarejestrować go atexit

#include <cstdlib> 

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

lub nazywają go bezpośrednio przed opuszczeniem 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; 
    ... 
} 
0

Sklepy VS wpisują informacje na pojedynczo połączonej liście. Nagłówek tej listy jest dostępny przez nieprzezroczystą strukturę dostępną pod nazwą __type_info_root_node. W rzeczywistości jest to struktura SLIST_HEADER.

Win32 API ma zestaw funkcji bezpieczeństwa współbieżnego do pracy z takimi strukturami. Aby naprawić raport wycieków pamięci W twoim przypadku musisz usunąć wszystkie węzły z tej listy.

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

Aktualizacja: VLD 2.5.1 nie zgłasza przecieki pamięci na type_info :: nazwie() w VS2015 Aktualizacji 3.

Powiązane problemy