2008-11-07 15 views
27

¿Los objetos Singleton que no usan los contadores de instancia/referencia se consideran fugas de memoria en C++?Singleton Destructors

Sin un contador que requiera la eliminación explícita de la instancia de singleton cuando el recuento es cero, ¿cómo se elimina el objeto? ¿Lo limpia el sistema operativo cuando finaliza la aplicación? ¿Y si ese Singleton hubiera asignado memoria en el montón?

En pocas palabras, ¿tengo que llamar al destructor de Singelton o puedo confiar en que se limpiará cuando finalice la aplicación?

Respuesta

14

Puede confiar en que el sistema operativo lo limpia.

Dicho esto, si se encuentra en un lenguaje basura con finalizadores en lugar de destructores, es posible que desee un procedimiento de apagado elegante que pueda cerrar directamente sus singletons para que puedan liberar recursos críticos en caso de que se utilicen recursos del sistema eso no será limpiado correctamente simplemente terminando la aplicación. Esto se debe a que los finalizadores se ejecutan en una especie de "mejor esfuerzo" en la mayoría de los idiomas. Por otro lado hay muy pocos recursos que necesitan este tipo de fiabilidad. manejadores de archivos, memoria, etc. todos regresan al sistema operativo limpiamente.

Si está utilizando un singleton asignado perezosamente (es decir, con un idioma de bloqueo de comprobación triple) en un lenguaje como C++ con destructores reales en lugar de finalizadores, no puede confiar en que se invoque su destructor durante el apagado del programa. Si está utilizando una única instancia estática, el destructor se ejecutará después de que el principal se complete en algún momento.

Independientemente, cuando el proceso finaliza, toda la memoria vuelve al sistema operativo.

0

Cualquier memoria de pila asignada por su proceso y no liberada (eliminada) será reclamada por el sistema operativo. Si está utilizando la implementación más común de singleton, que usa variables estáticas, esto también se eliminará cuando finalice la aplicación.

* Esto no significa que deba ir alrededor de los nuevos punteros y nunca limpiarlos.

1

A singleton sería una instancia de su objeto. Es por eso que no requiere un contador. Si va a existir para la duración de su aplicación, entonces el destructor predeterminado estará bien. La memoria será, en cualquier caso, recuperada por el sistema operativo cuando finalice el proceso.

3

Cualquier tipo de asignación, excepto aquellas en memorias compartidas, se limpian automáticamente por el sistema operativo cuando finaliza el proceso. Por lo tanto, no debería tener que llamar explícitamente al destructor singleton. En otras palabras no hay fugas ...

Además una implementación típica Singleton como el Meyers Singleton no sólo es seguro para subprocesos durante la inicialización en la primera llamada, sino también garantiza que AGRACIADO terminará cuando se cierra la aplicación (el destructor es invocado).

De todos modos, si la aplicación se envía una señal de UNIX (es decir: SIGTERM o SIGHUP) el comportamiento por defecto es para terminar el proceso sin tener que llamar a los destructores de objetos estáticos asignados (singletons). Para solucionar este problema para estas señales, es posible disponer de un controlador que llame a la salida, o disponer de una salida como dicho controlador: signal(SIGTERM,exit);

+0

Si confía en que la biblioteca de tiempo de ejecución destruya sus objetos estáticos después de retornos principales, y espera que sea posible usar el código en una DLL (Windows), entonces está ejecutando código durante DllMain, y la mayoría de las cosas podría me gusta hacer son inseguros. –

1

Depende de su definición de fuga.El aumento de la memoria sin consolidar es una filtración en mi libro, un singleton no está desatado. Si no proporciona recuento de referencias, mantiene intencionalmente la instancia activa. No es un accidente, no es una fuga.

El destructor de su contenedor único debe eliminar la instancia, no es automático. Si solo asigna memoria y no tiene recursos del sistema operativo, no tiene sentido.

11

Debería limpiar explícitamente todos sus objetos. Nunca confíe en el sistema operativo para hacer la limpieza por usted.

Donde suelo usar un singleton es para encapsular el control de algo como un archivo, recurso de hardware, etc. Si no limpio adecuadamente esa conexión, puedo fugar fácilmente los recursos del sistema. La próxima vez que se ejecute la aplicación, podría fallar si el recurso todavía está bloqueado por la operación anterior. Otro problema podría ser que cualquier finalización, como escribir un búfer en el disco, podría no ocurrir si todavía existe en un búfer propiedad de una instancia singleton.

Esto no es un problema de pérdida de memoria: el problema es más que puede estar perdiendo recursos que no sean de memoria y que no se puedan recuperar tan fácilmente.

+0

+1 de hecho, pero algunos singletons aseguran la llamada de destructor cuando la aplicación sale. –

+1

Si confía en que la biblioteca de tiempo de ejecución destruya sus objetos estáticos después de retornos principales, y espera que sea posible usar el código en una DLL (Windows), entonces está ejecutando código durante DllMain, y la mayoría de las cosas que le gustaría no son seguros –

9

Cada idioma y entorno diferirán, aunque estoy de acuerdo con @Aaron Fisher en que un singleton tiende a existir durante la duración del proceso.

En el ejemplo de C++, usando un típico modismo singleton:

Singleton &get_singleton() 
{ 
    static Singleton singleton; 
    return singleton; 
} 

la instancia Singleton se construirá la primera vez que la función se llama, y ​​la misma instancia tendrá que se llama destructor durante la estática mundial fase de destrucción al apagar el programa.

20

Como tantas veces, "depende". En cualquier sistema operativo digno de este nombre, cuando finaliza su proceso, se liberará toda la memoria y otros recursos utilizados localmente dentro del proceso. Simplemente no necesita preocuparse por eso.

Sin embargo, si su singleton está asignando recursos de por vida fuera de su propio proceso (tal vez un archivo, un nombre mutex o algo similar), entonces necesita considerar la limpieza adecuada.

RAII lo ayudará aquí. Si usted tiene un escenario como este:

class Tempfile 
{ 
Tempfile() {}; // creates a temporary file 
virtual ~Tempfile(); // close AND DELETE the temporary file 
}; 

Tempfile &singleton() 
{ 
    static Tempfile t; 
    return t; 
} 

... entonces usted puede estar seguro de que su archivo temporal será cerrado y eliminado sin embargo, su aplicación se cierra. Sin embargo However, this is NOT thread-safe, and the order of object deletion may not be what you expect or require.

, si su producto único se implementa de la siguiente

Tempfile &singleton() 
{ 
    static Tempfile *t = NULL; 
    if (t == NULL) 
    t = new Tempfile(); 
    return *t; 
} 

... entonces usted tiene una situación diferente. La memoria utilizada por el archivo temporal se recuperará, pero el archivo NO se eliminará porque no se invocará el destructor.

3

¿Cómo está creando el objeto?

Si está utilizando una variable global o una variable estática, se llamará al destructor, suponiendo que el programa sale normalmente.

Por ejemplo, el programa

#include <iostream> 

class Test 
{ 
    const char *msg; 

public: 

    Test(const char *msg) 
    : msg(msg) 
    {} 

    ~Test() 
    { 
     std::cout << "In destructor: " << msg << std::endl; 
    } 
}; 

Test globalTest("GlobalTest"); 

int main(int, char *argv[]) 
{ 
    static Test staticTest("StaticTest"); 

    return 0; 
} 

Imprime

In destructor: StaticTest 
In destructor: GlobalTest 
2

Es folclore para liberar las asignaciones de memoria globales explícitamente antes de la aplicación termina. Supongo que la mayoría de nosotros lo hacemos por costumbre y porque creemos que es una especie de "olvidar" una estructura. En el mundo C, es una ley de simetría que cualquier asignación debe tener una desasignación en alguna parte. Los programadores de C++ piensan diferente si conocen y practican RAII.

En los buenos tiempos de, p. AmigaOS había pérdidas de memoria REALES. Cuando olvidó desasignar la memoria, NUNCA volverá a estar accesible hasta que se restablezca el sistema.

No conozco ningún sistema operativo de escritorio que se precie en estos días que permita que las pérdidas de memoria salgan del espacio de direcciones virtuales de una aplicación. Su millaje puede variar en los dispositivos integrados cuando no hay una contabilidad extensa.

+1

No es folklore si escribe código reutilizable como parte de un esfuerzo de desarrollo de aplicaciones. Si no conoce todos los contextos futuros en los que el código que espera ser reutilizable se utilizará, entonces es mejor pecar de cauteloso. Mis propios proyectos tienden a ser principalmente códigos que espero reutilizar con una delgada capa de lógica de la aplicación. –

+0

Además, los programadores de C++ no obtienen un pase libre, incluso si son conocedores de RAII, especialmente en el contexto de singletons. Si confía en que la biblioteca de tiempo de ejecución destruirá sus objetos estáticos después de retornos principales, y espera que sea posible usar el código en una DLL (Windows), entonces está ejecutando código durante DllMain, y la mayoría de las cosas que le gustaría hacer son inseguros –

+0

Tiene razón para el código reutilizable dentro de las bibliotecas. Pero no para aplicaciones. No hay pérdidas de memoria fuera del código global (es decir, bibliotecas) en ningún sistema operativo moderno. Estos simplemente no existen. Toda la arena de la memoria se elimina cuando la aplicación finaliza. Todos los new() y malloc() se vuelven polvo, independientemente de free() o delete(). – Thorsten79

0

En idiomas como C++ que no tienen recolección de basura, se recomienda limpiar antes de la finalización. Puedes hacer esto con una clase de amigo destructor.

class Singleton{ 
... 
    friend class Singleton_Cleanup; 
}; 
class Singleton_Cleanup{ 
public: 
    ~Singleton_Cleanup(){ 
     delete Singleton::ptr; 
    } 
}; 

crear la clase de limpieza al iniciar el programa y luego al salir del destructor será llamado la limpieza de la Singleton. Esto puede ser más detallado que dejarlo ir al sistema operativo, pero sigue los principios de RAII y, dependiendo de los recursos asignados en su objeto singleton, podría ser necesario.