2011-11-29 16 views
8

Tengo un programa en el que, en parte para el registro informativo, obtengo los nombres de algunas clases a medida que se utilizan (específicamente, agrego una entrada a un registro que dice lo siguiente Messages::CSomeClass transmitted to 127.0.0.1). Lo hago con un código similar al siguiente:Fugas de memoria después de utilizar typeinfo :: name()

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

Y sí, antes de que nadie lo señala, me doy cuenta de que la salida de typeinfo::name es específico de la implementación.

Según MSDN

La función miembro type_info::name devuelve un const char* a una cadena terminada en nulo que representa el nombre legible por humanos del tipo. La memoria apuntada se almacena en caché y nunca se debe desasignar directamente.

Sin embargo, cuando salgo de mi programa en el depurador, aparece un "nuevo" uso de typeinfo::name() como una pérdida de memoria. Si genero la información para 2 clases, obtengo 2 pérdidas de memoria, y así sucesivamente. Esto sugiere que los datos en caché nunca se liberan.

Si bien esto no es un problema importante, parece desordenado, y después de una larga sesión de depuración podría ocultar fácilmente fugas de memoria genuinas.

He mirado alrededor y he encontrado información útil (una respuesta SO da información interesante sobre how typeinfo may be implemented), pero me pregunto si esta memoria normalmente debería ser liberada por el sistema, o si hay algo que pueda hacer para "no notar" las fugas cuando se depura.

Tengo un plan de copia de seguridad, que es codificar el método getMessageName y no confiar en typeinfo::name, pero me gustaría saber de todos modos si hay algo que me he perdido.

+1

Posiblemente relevante? [http://connect.microsoft.com/VisualStudio/feedback/details/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name](http://connect.microsoft.com/VisualStudio/ comentarios/detalles/106937/memory-leaks-reported-by-debug-crt-inside-typeinfo-name). Qué compilador estas usando? Tal vez intente con un compilador diferente si eso es posible. –

+0

Dado que está en caché, no se preocupe. – Jagannath

+0

@jagansai: No estoy preocupado por la filtración en sí misma, ya que solo afecta la salida del depurador al salir de la aplicación; mi preocupación es que podría ocultar pérdidas de memoria * real *. Y parece desordenado. Me gusta la depurada salida del depurador :) – icabod

Respuesta

4

Otra solución es corregir el problema subyacente. Esto no es realmente una pérdida de memoria, solo un informe falso. Los bloques de memoria asignados a las cadenas tyepinfo() y name() tienen asignado el tipo de bloque incorrecto. Probablemente no sea una buena idea "liberar" esta memoria, ya que el CRT intentará liberarla nuevamente. La buena noticia es que esto finalmente se solucionó en VS2012 (_MSC_VER 1700+).

Dado que esto solo se aplica a las compilaciones _DEBUG, la siguiente puede ser una solución más segura. La función _FixTypeInfoBlockUse() debe invocarse como se mencionó anteriormente justo antes de salir del punto de entrada del módulo (principal, 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) 
+0

Cambié mi respuesta aceptada a esto, ya que es un poco más limpio (?) Que las otras respuestas, en parte debido al '# si 'que lo limita a las compilaciones afectadas. – icabod

1

Como lo señaló Chris Parton en los comentarios, esto parece ser un known bug, al menos con la versión del compilador que estoy usando: la actualización a VC11 corregirá el problema, si pudiera actualizarlo.

El intento de eliminar la salida de typeinfo::name() funciona parcialmente:

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

Sin embargo todavía hay algunas pérdidas de memoria - Me he dado cuenta de que antes me parecía estar recibiendo dos fugas por llamada (tal vez debido a que las clases dentro de un espacio de nombres?). Usando la versión anterior del código, esto se redujo a una fuga por llamada.

Otra solución que parece funcionar es vincular en la versión dinámica de las bibliotecas MFC (sí, estoy usando MFC, no me juzgue), en lugar de la versión estática.

+0

Aceptando mi propia respuesta en ausencia de otras. – icabod

+0

Sí, obtienes una pérdida al borrar la memoria solo para una cadena. También se eliminará el nodo correspondiente de la lista vinculada individual. Ver mi respuesta – Stas

+0

Esta es una solución realmente mala ya que la cadena está almacenada en la memoria caché, por lo que si vuelve a llamar a typeid (* this) .name() después de eliminar, obtendrá una cadena de basura. –

4

Acabo de tropezar con este problema tratando de limpiar el registro de VLD. Sí, este es un known bug, que se soluciona solo en VC11. Existe en versiones anteriores de MSVC incluyendo 2010. Esta falla solo aparece si usa MFC. Si usa MFC como DLL en lugar de biblioteca estática, la pérdida de memoria seguirá existiendo, pero no se detectará.

Hay un caché global de type_info nombres y no se borra (el extracto de <typeinfo>):

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

extern __type_info_node __type_info_root_node; 

La idea es despejar esta caché. Esta función funciona para mí:

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

llamada clear_type_info_cache() antes de la salida. Usted puede colocarlo con atexit

#include <cstdlib> 

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

o llamar inmediatamente antes de dejar 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

VS tiendas de tipo de información en una lista de enlaces individuales. El encabezado de esta lista es accesible mediante una estructura opaca accesible por nombre __type_info_root_node. En realidad, es una estructura SLIST_HEADER.

Win32 API tiene un conjunto de funciones de concurrencia segura para trabajar con tales estructuras. Para reparar el informe de fugas de memoria En su caso, debe eliminar todos los nodos de esta lista.

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

Actualizado: VLD 2.5.1 no informa de pérdidas de memoria en type_info :: name() en VS2015 actualización 3.