2009-06-30 11 views
10

Uso boost :: shared_ptr en mi aplicación en C++. El problema de memoria es realmente grave y la aplicación requiere una gran cantidad de memoria.¿Cómo sé quién tiene el shared_ptr <>?

Sin embargo, como coloco cada objeto nuevo en shared_ptr, cuando la aplicación sale, no se puede detectar ninguna fuga de memoria.

Debe haber algo así como std::vector<shared_ptr<> > grupo que contiene el recurso. ¿Cómo puedo saber quién tiene el shared_ptr cuando se depura?

Es difícil revisar el código línea por línea. Demasiado código ...

¡Muchas gracias!

Respuesta

20

No puede saber, al solo mirar shared_ptr, donde están los "punteros de hermanos". Puede probar si uno es unique() o obtener el use_count(), entre other methods.

1

Obviamente, conservará referencias a sus objetos dentro de su aplicación. Esto significa que, a propósito, estás guardando cosas en la memoria. Eso significa que no tienes una pérdida de memoria. Una pérdida de memoria se produce cuando se asigna memoria, y luego no guarda una referencia a su dirección.

Básicamente, debe observar su diseño y descubrir por qué mantiene tantos objetos y datos en la memoria, y cómo puede minimizarlos.

La única posibilidad de que tenga una fuga de pseudo memoria es que está creando más objetos de lo que cree que es. Trate de poner puntos de corte en todas las declaraciones que contengan un 'nuevo'. Vea si su aplicación está construyendo más objetos de lo que pensaba que debería y luego lea ese código.

El problema no es tanto una fuga de memoria como un problema del diseño de su aplicación.

+1

¡Muchas gracias! Hay alrededor de 200 mil líneas. Por lo tanto, es difícil comprobar cada nuevo ... ¿hay alguna macro de compilación para activar la capacidad de verificación de ref de boost (si tal habilidad existe). La memoria está causando al programar error de lógica sobre pools, estoy seguro, pero simplemente no puedo encontrarlo. – user25749

+3

Aún puede tener pérdidas de memoria con shared_ptrs. Cree una referencia cíclica, y nunca se eliminará, incluso si el resto de la aplicación ya no hace referencia a ella. Fuga de memoria instantánea! – jalf

+5

Una referencia a un objeto retenido incorrectamente sigue siendo una pérdida de recursos. Esta es la razón por la cual los programas de GC aún pueden tener pérdidas, generalmente debido al patrón Observador: el observador se encuentra en una lista en lugar de ser observable y nunca se le quita. En definitiva, se necesita un 'eliminar' para cada 'agregar', al igual que se necesita 'eliminar' para cada 'nuevo'. Exactamente el mismo error de programación, causando exactamente el mismo problema. Un "recurso" es realmente solo un par de funciones que deben llamarse el mismo número de veces con los argumentos correspondientes, y una "fuga de recursos" es lo que sucede cuando no se puede hacer eso. –

3

Es posible que tenga una fuga de memoria de puntero compartida a través de ciclos. Lo que ocurre es que sus objetos compartidos pueden contener referencias a otros objetos compartidos que eventualmente conducen al original. Cuando esto sucede, el ciclo mantiene todos los recuentos de referencia en 1 a pesar de que nadie más puede acceder a los objetos. La solución es weak pointers.

+0

¡Muchas gracias! Realmente uso weak_ptr como un observador de recursos. Entonces sé que existe una gran cantidad de shared_ptr <> en la memoria. Estoy seguro de que no hay ciclos, algunos módulos están mal diseñados, estoy tratando de averiguarlo. – user25749

+2

"_La solución es punteros débiles._" No, no lo es. La solución es revisar el diseño. – curiousguy

+0

@curiousguy: Sí, sí, para ser pedante, la solución _real_ para la propiedad cíclica es evitarla. Sin embargo, hay problemas legítimos que se resuelven mejor con punteros cíclicos. En esos casos: una solución para una pérdida de memoria causada por una referencia cíclica verdaderamente necesaria es usar 'std :: weak_ptr'. –

3

Intente refacturar parte de su código para que la propiedad se exprese más explícitamente mediante el uso de punteros débiles en lugar de punteros compartidos en algunos lugares.

Al buscar en la jerarquía de su clase, es posible determinar qué clase debe contener un puntero compartido y cuál necesita solamente el débil, para evitar ciclos si los hay y si el objeto propietario "real" se destruye , los objetos "sin propietario" ya deberían haber desaparecido. Si resulta que algunos objetos pierden punteros demasiado pronto, debe buscar en la secuencia de destrucción de objetos en su aplicación y solucionarlo.

+0

Lo que está diciendo es que ** las referencias débiles se pueden usar para la depuración **: si la referencia débil está muerta, tenemos que usarla, significa que el programa tiene un error. Esta es la primera vez que veo esta idea claramente expresada en el sistema operativo: algunos otros usos de los carteles de 'weak_ptr' parecen implicar que es una herramienta de depuración, pero no lo dicen con claridad. Observación: eso es menos eficiente que usar un puntero normal de C++: tal vez necesitamos un 'checked_ptr' que se pueda definir como' weak_ptr' o como un puntero inteligente de "puntero regular". – curiousguy

-1

No es posible decir qué objetos poseen shared_ptr dentro del programa. Si está en Linux, una forma segura de depurar las pérdidas de memoria es la herramienta Valgrind - aunque no responderá directamente a su pregunta, le indicará dónde se asignó la memoria, que generalmente es suficiente para solucionar el problema. Me imagino que Windows tiene herramientas comparables, pero no sé cuál es la mejor.

11

El popular uso generalizado de shared_ptr casi inevitablemente causará una ocupación de memoria no deseada e invisible.

Las referencias cíclicas son una causa bien conocida y algunas de ellas pueden ser indirectas y difíciles de detectar, especialmente en código complejo en el que trabaja más de un programador; un programador puede decidir que un objeto necesita una referencia a otro como una solución rápida y no tiene tiempo para examinar todo el código para ver si está cerrando un ciclo. Este peligro está muy subestimado.

Menos conocido es el problema de las referencias inéditas. Si un objeto se comparte en muchos shared_ptrs, no se destruirá hasta que cada uno de ellos se ponga a cero o salga del alcance. Es muy fácil pasar por alto una de estas referencias y terminar con objetos ocultos en la memoria que usted pensó que había terminado.

Aunque estrictamente hablando, estas no son pérdidas de memoria (todo se lanzará antes de que el programa salga) son igual de dañinas y más difíciles de detectar.

Estos problemas son consecuencia de declaraciones falsas oportunas: 1. Declarando lo que realmente quiere ser propiedad única como shared_ptr. scoped_ptr sería correcto, pero luego cualquier otra referencia a ese objeto tendrá que ser un puntero sin formato, que podría dejarse colgando. 2. Declarando lo que realmente quiere que sea una referencia de observación pasiva como shared_ptr. weak_ptr sería correcto, pero tienes la molestia de convertirlo a share_ptr cada vez que quieras usarlo.

Sospecho que su proyecto es un buen ejemplo del tipo de problemas en los que puede incurrir esta práctica.

Si tiene una aplicación con uso intensivo de memoria, realmente necesita una sola propiedad para que su diseño pueda controlar explícitamente la duración de los objetos.

Con propiedad única opObject = NULL; definitivamente eliminará el objeto y lo hará ahora.

Con propiedad compartida spObject = NULL; ........ ¿quién sabe? ......

+0

"_un programador puede decidir que un objeto necesita una referencia a otro como una solución rápida y no tiene tiempo para examinar todo el código para ver si está cerrando un ciclo._" No tiene que "leer el código" . Él debe leer los ** documentos de diseño **. – curiousguy

+4

en el mundo real muchos proyectos no tienen ningún concepto de documentos de diseño :) – paulm

+3

@curiousguy Prefiero el código para diseñar documentos, ya que los documentos pueden mentir, y compila el código. – M2tM

1

Iba a sugerir el uso de UMDH si está en Windows. Es una herramienta muy poderosa. Úselo para encontrar asignaciones por transacción/período de tiempo que espera que se libere y luego encuentre quién está reteniéndolas.

Hay más información sobre esta respuesta, así Find memory leaks caused by smart pointers

5

Una solución a colgando o referencias circulares puntero inteligente que hemos hecho es personalizar la clase puntero inteligente para añadir una única función de depuración de contabilidad. Cada vez que un SmartPointer añade una referencia a un objeto, se necesita un seguimiento de la pila y lo pone en un mapa cuyos cada entrada lleva un registro de

  1. La dirección del objeto que se asigna (lo que el puntero apunta a)
  2. las direcciones de cada SmartPointer objeto que sostiene una referencia al objeto
  3. los correspondientes stacktraces de cuando cada SmartPointer se construyó

cuando un SmartPointer sale del ámbito, su entrada en el mapa se elimina. Cuando se destruye el último smartpointer para un objeto, el objeto puntuado obtiene su entrada en el mapa eliminado.

Luego tenemos un comando "rastrear fugas" con dos funciones: '[reiniciar] rastreo de fugas' (que borra todo el mapa y habilita el rastreo si aún no), y 'imprimir referencias abiertas', que muestra todo referencias destacadas de smartpointer creadas desde que se emitió el comando 'start leak tracking'. Como puede ver los rastros de pila de dónde surgieron esos indicadores inteligentes, puede saber fácilmente quién se está liberando de su objeto. Reduce las cosas cuando está encendido, por lo que no lo dejamos todo el tiempo.

Es una gran cantidad de trabajo para implementar, pero definitivamente vale la pena si tienes una base de código donde esto sucede mucho.

Cuestiones relacionadas