2012-10-10 38 views
6

que estoy tratando de imprimir en el que AddRef línea y la liberación es called.Here es el códigopérdida de memoria en referencia contó objetos

En código de abajo he creado en la clase ReferenceCount cuya principal funcionalidad para aumentar y disminuir el recuento refernce. La clase Referencemanager realiza un seguimiento del recuento de referencias y elimina el objeto una vez que alcanza 0.

Test1 es la clase de prueba. En main, estoy creando el puntero Test1 y lo ajuste con la clase CReferenceManager. Ahora, durante la creación de la clase CReferenceManager, se llama a AddRef y mientras se llamará a Release de destrucción.

Si hay una pérdida de memoria, sería más fácil detectar si puedo imprimir números FILE y LINE cuando AddRef y Release se llaman con recuentos de referencia en ese punto.

Si hay una forma de que pueda imprimir el número de ARCHIVO y LÍNEA desde donde se llama AddRef y Release. Una forma es que puedo sobrescribir AddRef y liberación de las clases derivadas y archivo y linea números prinf

preguntas
//ReferenceCount.h 
#include <string> 
#include <Windows.h> 

using namespace std; 
class CReferenceCount 
{ 
public: 
    CReferenceCount(); 
    virtual ~CReferenceCount(); 
    virtual void AddRef(); 
    virtual bool Release(); 


private: 
    LONG m_ref; 

}; 


// RefCount.cpp 
// 

#include "stdafx.h" 
#include "ReferenceCount.h" 


CReferenceCount::CReferenceCount():m_ref(0) 
{ 
    AddRef(); 

} 

CReferenceCount::~CReferenceCount() 
{ 
} 

void CReferenceCount::AddRef() 
{ 
    InterlockedIncrement(&m_ref); 
} 

bool CReferenceCount::Release() 
{ 
    if (InterlockedDecrement(&m_ref) == 0) 
    { 
     delete this; 
     return true; 
    } 

    return false; 
} 



//ReferenceManager.h 
#include <string> 
#include <Windows.h> 

using namespace std; 
class CReferenceCount 
{ 
public: 
    CReferenceCount(); 
    virtual ~CReferenceCount(); 
    virtual void AddRef(); 
    virtual bool Release(); 


private: 
    LONG m_ref; 

}; 

//test.cpp 
#include "stdafx.h" 
#include "ReferenceCount.h" 
#include "RefManager.h" 
#include <iostream> 
using namespace std; 

class Test1: public CReferenceCount 
{ 
public: 
    Test1(){} 
    ~Test1(){} 

private : 
    int m_i; 
}; 

void main() 
{ 
    Test1 *pTest= new Test1(); 
    CReferenceManager<Test1> testRef(pTest); 

} 

Similare He publicado finding who creates object via smart pointer Design pattern to detect memory leaks for reference counted smart pointers

pero no de las respuestas dar explicación correcta de abordar este proble,

+9

'eliminar esto;' OMG !!! –

+0

¿Utiliza el puntero inteligente para llamar a AddRef/Release o los llama manualmente? Si los llamas manualmente, recomiendo encarecidamente no hacerlo. –

+0

una plantilla sería una mejor solución; la plantilla se puede hacer para que se ajuste a cada objeto, al igual que una std :: shared_ptr –

Respuesta

6

La única forma es definir macros para llamar a AddRef y Release, ya que no hay forma de que las funciones sepan internamente desde donde se llaman. Entonces podrías usar algo como.

#define RELEASE(obj) cout << __LINE__ << ":" << __FILE__ << endl; (obj).Release(); 

Además, los diferentes compiladores tienen diferentes macros predefinidas; si la portabilidad es una preocupación, es algo que debe considerar al escribir código como el anterior. MSDN reference (2003)

Dado sus comentarios a continuación, podría ofrecer otra solución algo hackish. Es posible que no pueda ver dónde se está divulgando su referencia, pero puede obtener más información sobre dónde se creó y cuáles no se están publicando correctamente.

template <typename T> 
struct CReferenceManager 
{ 
    CReferenceManager(const T & _obj, const string & _file, int _line) : mObj(_obj), mFile(_file), mLine(_line) 
    { 
     cout << "Constructing from " << _file << ":" << _line << endl; 
     CReferenceManager::sObjects[make_pair(mFile, mLine)]++; 
     mObj.addRef(); 
    } 

    ~CReferenceManager() 
    { 
     cout << "Destructing object created at " << mFile << ":" << mLine << endl; 
     CReferenceManager::sObjects[make_pair(mFile, mLine)]--; 
     mObj.Release(); 
    } 

    static map<pair<string, int>, int> sObjects; 
    string mFile; 
    int mLine; 
    T obj; 
} 

int main() 
{ 
... 
    // Cycle through sObjects before return, note any unreleased entries 
    return 0; 
} 

Nota esto es solo pseudo-código; ¡Dudo que compila o funciona de la caja!

+0

Esto es lo que aconsejé aquí, ayer: http://stackoverflow.com/a/12806087/241536 –

+0

Más o menos .. –

+0

Si utilizo esta macro, se imprimirá FILE como RefManager y no como test.cpp. Mi intención es imprimir test.cpp y su número de línea desde donde se llamará a Release. – anand

2

Hay alguna forma de hacerlo, pero primero déjame preguntarte una cosa. ¿Por qué quiere administrar referencias a mano y proporcionar una oportunidad para pérdidas de memoria? puede usar fácilmente boost::intrusive_ptr para hacer el trabajo por usted (si no desea el impulso, no hay problema, consulte la implementación de intrusive_ptr e implemente su propia clase o simplemente cópiela en su propio archivo) y luego no lo haga. ¡Tengo una pérdida de memoria para buscarlo!

sino como una respuesta a su pregunta podría tener 2 AddRef/Release otro para la versión de depuración y otro para la liberación y se debe añadir AddRef posiciones a una estructura como std::stack y en Release hacerlas explotar de stack y al mismo final que vea cuánto ¡las referencias de las posiciones de bruja permanecieron en la pila! pero si esto es para la implementación de COM, recuerde que COM puede llamar al AddRef varias veces y luego eliminarlas en un momento posterior y, por lo tanto, no puede comprender qué AddRef no tiene Release correspondiente.

5

Nunca se debe asignar o liberar las referencias explícitamente en su propio código, por lo que almacena el archivo de origen y la línea donde las referencias se incrementa o disminuye no va a ayudarle en absoluto, ya que los hará (debe !) siempre estará dentro del código de administración de conteo de referencia.

no ha incluido el código fuente de la clase CReferenceManager, pero en base a su descripción es una envoltura a un objeto contado hace referencia. ¿Es esto correcto? La aplicación correcta de este objeto CReferenceManager debe garantizar que:

  • un constructor que toma unos desnudos tiendas puntero del puntero y no cambia la cuenta de referencia (ya que su clase CReferenceCount crea objeto con una referencia)
  • referencia es siempre decrementa en el destructor
  • referencia se incrementa en el constructor de copia
  • se incrementa de referencia para el objeto lado derecho, y de referencia para el objeto lado izquierdo se decrementa en el operador de asignación
  • no hay métodos de referencia de aumento/disminución explícitas deben ser expuestos
  • el operador método ->() debe devolver el puntero al objeto
  • no debe haber ninguna forma directa para separar el objeto contado de referencia desde una instancia CReferenceManager que lo posee . La única forma es a través de la asignación de un nuevo objeto contado de referencia.

Además, usted quiere hacer las AddRef() y Release() métodos de su clase CReferenceCount privada, y hacerlos accesibles sólo a la clase a través de la amistad CReferenceManager clase.

Si usted sigue las reglas anteriores en su clase CReferenceManager, entonces se puede evitar fugas u otros problemas de memoria, asegurando que todo el mundo tiene acceso al objeto a través de un envoltorio CReferenceManager asignado en la pila. En otras palabras:

Para crear un nuevo objeto contado referenciada, pasado un objeto recién creado (con una referencia) a un objeto CReferenceManager pila asignado. Ejemplo:

CReferenceManager<Test1> testRef(new Test1()); 

pasar el objeto como un argumento a otra función o método, siempre pasar un objeto CReferenceManager por valor (no por referencia, y no por puntero). Si lo haces de esta manera, el constructor de copias y el destructor se encargarán de mantener los recuentos de referencia por ti. Ejemplo:

void someFunction(CReferenceManager<Test1> testObj) 
{ 
    // use testObj as if it was a naked pointer 
    // reference mananagement is automatically handled 
    printf("some value: %d\n", testObj->someValue()); 
} 

int main() 
{ 
    CReferenceManager<Test1> testRef(new Test1()); 
    someFunction(testRef); 
} 

Si es necesario atenerse al objeto contado referencia en un recipiente, a continuación, insertar un envoltorio CReferenceManager por valor (no su puntero, y no desnudos puntero del objeto). Ejemplo:

std::vector< CReferenceManager<Test1> > myVector; 
CReferenceManager<Test1> testRef(new Test1()); 
myVector.push_back(testRef); 
myVector[0]->some_method(); // invoke the object as if it was a pointer! 

Creo que si se siguen estrictamente las reglas anteriores los únicos problemas que encontrará son los errores en la implementación de recuento de referencias.

Una implementación de ejemplo que sigue estas reglas está en this page, aunque esa solución carece de soporte para la protección de subprocesos múltiples.

Espero que esto ayude!

1

El principio del recuento de referencias es aumentar el contador cuando el usuario se vincula al objeto y disminuir cuando se rompe el enlace.

Así que hay que:

  • manipular punteros inteligentes, no los punteros para hacer aumentar/disminuir transparente
  • copia sobrecarga de constructor y asignar operador del smart_pointer

exemple simbólico:

  • A a = new A(); refcount = 0, nadie lo usa
  • Link<A> lnk(a); refcount = 1
  • obj.f(lnk); obj tiendas LNK, refcount = 2
  • este método puede se ha transferido retornos desde la propiedad a obj

tanto, eche un vistazo a paso de parámetros (puede hacer copias automáticas) y al copiar en objetos extraños.

Existen buenos tutoriales sobre eso en la nebulosa CORBA.

También puede ver ACE o ICE, o 0MQ.

2

Para los proyectos en los que participo, tenía necesidades similares. Tenemos nuestra propia clase de plantilla de puntero inteligente y de vez en cuando aparecieron fugas de memoria debido a referencias circulares.

para saber qué inteligente triple referencia a un objeto filtrado todavía está vivo (2 o más), compilamos las fuentes con un pre-procesador especial que permite definir el código de depuración especial en la ejecución-puntero inteligente. Puede echar un vistazo a nuestro smart-pointer class.

En esencia, cada puntero inteligente y objeto contado de referencia obtienen una identificación única. Cuando obtenemos la identificación para el objeto filtrado (usualmente usando valgrind para identificar la ubicación de origen de la asignación de memoria para el objeto filtrado), usamos nuestro código de depuración especial para obtener todos los identificadores de puntero inteligente que hacen referencia al objeto. Luego usamos un archivo de configuración donde anotamos los identificadores de los punteros inteligentes y en la siguiente puesta en marcha de la aplicación, nuestra herramienta de depuración lee este archivo y luego sabe para qué instancia de puntero inteligente recién creada debe activar una señal para ingresar al depurador Esto revela el seguimiento de pila donde se creó esa instancia de puntero inteligente.

Es cierto que esto implica un poco de trabajo y puede que solo rinda frutos en proyectos de mayor envergadura.

Otra posibilidad sería registrar un seguimiento de pila dentro de su método AddRef en tiempo de ejecución. Eche un vistazo a mi clase ctkBackTrace para crear un seguimiento de pila en tiempo de ejecución. Debería ser fácil reemplazar los tipos específicos de Qt por tipos estándar de STL.

1

Una manera de hacer lo que le pide, es pasar AddRef y liberación de esta información usando algo como esto:

void CReferenceCount::AddRef(const char *file=0, int line=-1) { if (file) cout << "FILE:" << file; if (line>0) count << " LINE: " << line; .... do the rest here ... } 

Luego, cuando se llama a la función, se puede utilizar una macro similar a lo que Rollie sugirió anteriormente , como este:

#define ADDREF(x) x.AddRef(__FILE__, __LINE__) 

Esto pasará el archivo y la línea donde se realiza la llamada, que creo que es lo que usted solicitó. Puede controlar lo que quiere hacer con la información dentro de los métodos. Imprimirlos, como hice anteriormente, es solo un ejemplo. Es posible que desee recopilar más información y registrarla en otro objeto, para que tenga un historial de sus llamadas, escribirlas en un archivo de registro, etc. También puede pasar más información de los puntos de llamada que solo el archivo y línea, de acuerdo con el tipo y el nivel de seguimiento que necesita. Los parámetros predeterminados también le permiten usarlos sin pasar nada (por una simple redefinición de macros), solo para ver cómo se comportará la versión final, con la sobrecarga de dos aprietamientos de pila y dos comprobaciones de condición.

+0

Como se señala en un comentario a la respuesta de Rollie, esto no funcionará ya que el OP necesita poner la macro llamada en su clase de plantilla CReferenceManager. Por lo tanto, la macro siempre imprimirá RefManager como el nombre del archivo. – Sascha

+0

Um, no. funcionará si la macro está en un archivo de cabecera y se usa en muchos otros archivos cpp. – DNT

+0

El espacio aquí no es suficiente para pegar una ejecución de ejemplo, pero puedo agregar una respuesta si a alguien le interesa. – DNT

1

Respuesta corta: debe utilizar las ideas que otros publican, es decir, haciendo uso de las macros ADD/liberación y que pasan a la predefinido _ _ ARCHIVO _ _ y _ _ LINEA _ _ macros que el compilador proporciona a su clase de seguimiento.

Respuesta un poco más larga: También puede usar la funcionalidad que le permite recorrer la pila y ver quién llamó a la función, que es algo más flexible y limpia que el uso de macros, pero casi con certeza más lenta.

Esta página muestra cómo lograr esto al usar GCC: http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/.

En Windows puede usar algunos intrínsecos del compilador junto con la funcionalidad de búsqueda de símbolos. Para más detalles visita: http://www.codeproject.com/tools/minidump.asp

Nótese que en ambos casos, sería preciso que su programa para incluir al menos algunos símbolos para que esto funcione.

A menos que tenga requisitos especiales para hacer esto en tiempo de ejecución, le sugiero que revise la respuesta breve.