Pregunta

Tengo un programa en el que, en parte para el registro informativo, envío los nombres de algunas clases a medida que se usan (específicamente agrego una entrada a un registro que dice en la línea de Messages::CSomeClass transmitted to 127.0.0.1). Hago esto con código similar al siguiente:

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

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

De acuerdo a MSDN

los type_info::name La función miembro devuelve un const char* a una cadena terminada nula que representa el nombre legible por humanos del tipo. La memoria apuntada es almacenada en caché y nunca debe estar directamente repartida.

Sin embargo, cuando salgo de mi programa en el depurador, cualquier "nuevo" uso de typeinfo::name() aparece como una fuga de memoria. Si envío la información para 2 clases, obtengo 2 fugas de memoria, etc. Esto sugiere que los datos almacenados en caché nunca se están liberando.

Si bien este no es un problema importante, se ve desordenado, y después de una larga sesión de depuración podría ocultar fácilmente las fugas de memoria genuina.

He mirado a su alrededor y encontré algo de información útil (una, por lo que la respuesta proporciona información interesante sobre Cómo se puede implementar typeInfo), pero me pregunto si el sistema normalmente debería liberar esta memoria, o si hay algo que puedo hacer para "no notar" las filtraciones al depurar.

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

¿Fue útil?

Solución

Otra solución es corregir el problema subyacente. Esto no es realmente una fuga de memoria, solo un informe falso. A los bloques de memoria asignados a la cadena tyepinfo () y el nombre () se les asigna el tipo de bloque incorrecto. Probablemente no sea una buena idea "liberar" este recuerdo, ya que el CRT intento un intento de liberarlo nuevamente. La buena noticia es que finalmente se solucionó en VS2012 (_msc_ver 1700+).

Dado que esto solo se aplica a las compilaciones de _debug, lo siguiente puede ser una solución más segura. La función _fixtypeInfoblockuse () debe llamarse como se mencionó anteriormente antes de salir del punto de entrada del módulo (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)

Otros consejos

Me he topado con este problema tratando de limpiar el registro de Vld. Si, esto es un error conocido, que se fija solo en VC11. Existe en versiones anteriores de MSVC, incluidos 2010. Este error aparece solo si usa MFC. Si usa MFC como DLL en lugar de la biblioteca estática, la fuga de memoria aún existirá, 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 borrar este 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;
   }
}

Llamar clear_type_info_cache() antes de la salida. Puedes registrarlo con atexit

#include <cstdlib>

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

o llámalo inmediatamente antes de dejar a 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;
   ...
}

Como señaló Chris Parton en los comentarios, esto parece ser un error conocido, al menos con la versión del compilador que estoy usando: actualizar a VC11 corregiría el problema, si pudiera actualizar.

Intentando 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 filtraciones de memoria: me di cuenta de que anteriormente parecía recibir dos fugas por llamada (¿tal vez debido a que las clases están 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 juzguen), en lugar de la versión estática.

Vs tiendas Tipo de información en una lista vinculada individualmente. El encabezado de esta lista es accesible por una estructura opaca accesible por su nombre __type_info_root_node. En realidad es una estructura SLIST_HEADER.

Win32 API tiene un conjunto de función segura para la concurrencia para trabajar con tales estructuras. Para arreglar 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 filtraciones de memoria en type_info :: name () en VS2015 Actualización 3.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top