¿Hasta dónde pueden llegar las pérdidas de memoria?

118

Me he encontrado con pérdidas de memoria muchas veces. Por lo general, cuando estoy mallochablando como si no hubiera mañana, o colgando FILE *es como ropa sucia. Generalmente asumo (léase: espero desesperadamente) que toda la memoria se limpia al menos cuando el programa termina. ¿Hay situaciones en las que la memoria filtrada no se recopilará cuando el programa finalice o se bloquee?

Si la respuesta varía mucho de un idioma a otro, centrémonos en C (++).

Tenga en cuenta el uso hiperbólico de la frase, "como si no hubiera mañana" y "colgando ... como ropa sucia". La inseguridad * malloc* puede dañar a sus seres queridos. Además, tenga cuidado con la ropa sucia.

DilithiumMatrix
fuente
3
Si está ejecutando un sistema operativo "moderno" como Linux o Windows, el sistema operativo mismo resolverá cualquier memoria no publicada cuando el programa finalice.
Oliver Charlesworth
60
En lugar de malloc -ing como si no hubiera un mañana, intenta fingir que hay un mañana y lleva un registro de tu memoria.
William Pursell
8
@WilliamPursell ah, entonces estás diciendo que a uno le callocgustaría que no hubiera mañana. Excelente.
DilithiumMatrix
8
"Si la respuesta varía mucho de un idioma a otro, centrémonos en c (++)". ¡c y c ++ no son el mismo idioma!
Johnsyweb
11
@zhermes: Comentar que C y C ++ son lenguajes diferentes esconde más de lo que crees ... En C ++ preferirás aprovecharte de los objetos con duración de almacenamiento automático, sigue el idioma de RAII ... dejas que estos objetos se ocupen de la memoria gestión para usted.
LihO

Respuestas:

111

No. Los sistemas operativos liberan todos los recursos retenidos por los procesos cuando salen.

Esto se aplica a todos los recursos que mantiene el sistema operativo: memoria, archivos abiertos, conexiones de red, identificadores de ventanas ...

Dicho esto, si el programa se ejecuta en un sistema integrado sin un sistema operativo, o con un sistema operativo muy simple o con errores, la memoria podría no ser utilizable hasta que se reinicie. Pero si estuviera en esa situación, probablemente no estaría haciendo esta pregunta.

El sistema operativo puede tardar mucho en liberar ciertos recursos. Por ejemplo, el puerto TCP que utiliza un servidor de red para aceptar conexiones puede tardar unos minutos en liberarse, incluso si el programa lo cierra correctamente. Un programa en red también puede contener recursos remotos como objetos de base de datos. El sistema remoto debería liberar esos recursos cuando se pierde la conexión de red, pero puede tardar incluso más que el sistema operativo local.

Joni
fuente
5
Un paradigma común en los RTOS es el modelo de proceso único, múltiples subprocesos y sin protección de memoria entre 'tareas'. Normalmente hay un montón. Ciertamente, así es como solía funcionar VxWorks, y probablemente todavía lo hace.
marko
29
Tenga en cuenta que el sistema operativo no puede liberar todos los recursos. Las conexiones de red, las transacciones de la base de datos, etc., no cerrarlas explícitamente pueden causar algunos resultados no deseados. No cerrar la conexión de red puede hacer que el servidor piense que todavía está activo por un período de tiempo indefinido, y para servidores que limitan el número de conexiones activas, puede causar accidentalmente la denegación de servicio. No cerrar las transacciones de la base de datos puede provocar la pérdida de datos no confirmados.
Lie Ryan
1
@Marko: la versión reciente de vxWorks ahora admite RTP (procesos en tiempo real) que admiten protección de memoria.
Xavier T.
20
"Los sistemas operativos liberan todos los recursos retenidos por los procesos cuando salen". No es estrictamente cierto. Por ejemplo, en (al menos) Linux, los semáforos SysV y otros objetos IPC no se limpian al salir del proceso. Por eso hay ipcrmuna limpieza manual, linux.die.net/man/8/ipcrm .
sleske
7
Además, si un objeto tiene un archivo temporal que mantiene, claramente no se limpiará después.
Mooing Duck
47

El estándar C no especifica que la memoria asignada por mallocse libera cuando el programa termina. Esto lo hace el sistema operativo y no todos los sistemas operativos (generalmente están en el mundo integrado) liberan la memoria cuando el programa termina.

ouah
fuente
20
Eso es más o menos porque el estándar C habla de programas C, no de los sistemas operativos en los que se ejecuta C ...
vonbrand
5
@vonbrand El estándar C podría haber tenido un párrafo que dice que cuando se maindevuelve, mallocse libera toda la memoria asignada por . Por ejemplo, dice que todos los archivos abiertos se cierran antes de que finalice el programa. Para la memoria asignada my malloc, simplemente no se especifica. Ahora, por supuesto, mi oración con respecto al sistema operativo describe lo que generalmente se hace, no lo que prescribe el Estándar, ya que no especifica nada sobre esto.
ouah
Permítanme corregir mi comentario: el estándar habla de C, no de cómo se inicia y se detiene el programa. Puede escribir muy bien un programa en C que se ejecute sin un sistema operativo. En ese caso, no hay nadie que haga la limpieza. El estándar muy deliberadamente no especifica nada menos que sea necesario, a fin de no restringir usos sin necesidad.
vonbrand
2
@ouah: " cuando main regresa ...". Eso es una suposición. Tenemos que considerar " si los retornos principales ...". std::atexittambién considera la terminación del programa vía std::exit, y luego también está std::aborty (específico de C ++) std::terminate.
MSalters
@ouah: Si eso hubiera sido incluido, atexitno sería utilizable. :-)
R .. GitHub DEJA DE AYUDAR A ICE
28

Como todas las respuestas han cubierto la mayoría de los aspectos de su pregunta sobre los sistemas operativos modernos, pero históricamente, hay uno que vale la pena mencionar si alguna vez ha programado en el mundo de DOS. Los programas Terminante y Permanente Residente (TSR) generalmente devuelven el control al sistema, pero residen en la memoria que podría reactivarse por una interrupción de software / hardware. Era normal ver mensajes como "¡sin memoria! Intenta descargar algunos de tus TSR" cuando trabaja en estos sistemas operativos.

Entonces técnicamente el programa termina , pero debido a que aún reside en la memoria, cualquier pérdida de memoria no se liberará a menos que descargue el programa.

Por lo tanto, puede considerar que este es otro caso, además de que los sistemas operativos no recuperan la memoria, ya sea porque tiene errores o porque el sistema operativo integrado está diseñado para hacerlo.

Recuerdo un ejemplo más. Customer Information Control System (CICS), un servidor de transacciones que se ejecuta principalmente en mainframes IBM es pseudo-conversacional. Cuando se ejecuta, procesa los datos ingresados ​​por el usuario, genera otro conjunto de datos para el usuario, se transfiere al nodo terminal del usuario y finaliza. Al activar la tecla de atención, vuelve a reactivarse para procesar otro conjunto de datos. Debido a la forma en que se comporta, técnicamente nuevamente, el sistema operativo no recuperará memoria de los Programas CICS terminados, a menos que recicle el servidor de transacciones CICS.

Abhijit
fuente
Eso es realmente interesante, ¡gracias por la nota histórica! ¿Sabe si ese paradigma se debió a que liberar memoria era demasiado costoso computacionalmente si no fuera necesario? ¿O simplemente nunca se había pensado en la alternativa?
DilithiumMatrix
1
@zhermes: Era computacionalmente imposible, ya que DOS simplemente no rastreaba las asignaciones de memoria para los TSR. Básicamente por definición: el objetivo era permanecer residente . Si deseaba que su TSR liberara parte de la memoria, pero no toda, dependía de usted decidir qué liberar.
MSalters
2
@zhermes: DOS (como CP / M, su antepasado) no era lo que llamarías un sistema operativo en el sentido moderno. En realidad, era solo una colección de utilidades de E / S a las que se podía llamar de manera estándar junto con un procesador de comandos que le permitía ejecutar un programa a la vez. No existía la noción de procesos y la memoria no era virtual ni estaba protegida. Los TSR eran un truco útil que podía decirle al sistema que estaban ocupando hasta 64 K de espacio y se conectarían a las interrupciones para que los llamaran.
Blrfl
8

Como han dicho los demás, la mayoría de los sistemas operativos recuperarán la memoria asignada al finalizar el proceso (y probablemente otros recursos como sockets de red, identificadores de archivos, etc.).

Dicho esto, es posible que la memoria no sea lo único de lo que deba preocuparse cuando trabaje con new / delete (en lugar de raw malloc / free). La memoria asignada en nuevo puede ser reclamada, pero las cosas que se pueden hacer en los destructores de los objetos no sucederán. Quizás el destructor de alguna clase escribe un valor centinela en un archivo al ser destruido. Si el proceso simplemente termina, el identificador del archivo puede vaciarse y la memoria recuperada, pero ese valor centinela no se escribiría.

Moraleja de la historia, siempre limpia después de ti mismo. No dejes que las cosas cuelguen. No confíe en la limpieza del sistema operativo después de usted. Limpia después de ti mismo.

Andre Kostur
fuente
No confíe en la limpieza del sistema operativo después de usted. Limpia después de ti mismo. Esto a menudo es imp ... 'muy, muy difícil' con aplicaciones complejas multiproceso. Las fugas reales, donde se han perdido todas las referencias a un recurso, son malas. Permitir que el sistema operativo limpie en lugar de publicar referencias explícitamente no siempre es malo y, a menudo, es el único camino razonable a seguir.
Martin James
1
En C ++, se llamará a los destructores al finalizar el programa (a menos que kill -9aparezca un fan menos que brillante ...)
vonbrand
@vonbrand Es cierto, pero si estamos hablando de fugas con objetos dinámicos, esos destructores no ocurrirán. El objeto que sale del alcance es un puntero sin formato y su destructor es un no-op. (Por supuesto, vea los objetos RAII para mitigar este problema ...)
Andre Kostur
1
El problema con RAII es que insiste en desasignar objetos al salir del proceso de los que no es realmente importante deshacerse. Conexiones de base de datos con las que debe tener cuidado, pero la memoria general se limpia mejor con el sistema operativo (hace un trabajo mucho mejor). El problema se manifiesta como un programa que tarda muchísimo en salir una vez que aumenta la cantidad de memoria paginada. Tampoco es trivial de resolver…
Donal Fellows
@vonbrand: No es tan simple. std::exitllamará a dtors, std::abortno lo hará, las excepciones no detectadas podrían.
MSalters
7

Es más probable que esto dependa del sistema operativo que del idioma. En última instancia, cualquier programa en cualquier idioma obtendrá su memoria del sistema operativo.

Nunca he oído hablar de un sistema operativo que no recicla la memoria cuando un programa se cierra o falla. Entonces, si su programa tiene un límite superior en la memoria que necesita asignar, entonces simplemente asignar y nunca liberar es perfectamente razonable.

Juan
fuente
¿Podrías estropear la imagen de la memoria del kernel en caso de un sistema operativo simplista? ... Como esos sistemas operativos sin siquiera multitarea.
ulidtko
@ulidtko, esto va a enredar las cosas. Si mi programa requiere decir 1GiB de vez en cuando, y lo toma durante el tiempo, está negando el uso de esos recursos a otros incluso cuando no lo están usando. Eso podría importar hoy, o no. Pero el ambiente será cambiar radicalmente. Garantizado.
vonbrand
@vonbrand El uso poco frecuente de 1GiB no es un problema normalmente (siempre que tenga suficiente memoria física) ya que los sistemas operativos modernos pueden eliminar los bits que no están activos actualmente. El problema surge cuando tiene más memoria virtual en uso activo que memoria física en la que alojarla.
Donal Fellows
5

Si el programa alguna vez se convierte en un componente dinámico ("complemento") que se carga en el espacio de direcciones de otro programa, será problemático, incluso en un sistema operativo con una gestión de memoria ordenada. Ni siquiera tenemos que pensar en la portabilidad del código a sistemas menos capaces.

Por otro lado, liberar toda la memoria puede afectar el rendimiento de la limpieza de un programa.

Un programa en el que estaba trabajando, cierto caso de prueba, requirió 30 segundos o más para que el programa saliera, porque estaba recursándose a través del gráfico de toda la memoria dinámica y liberándolo pieza por pieza.

Una solución razonable es tener la capacidad allí y cubrirla con casos de prueba, pero desactivarla en el código de producción para que la aplicación se cierre rápidamente.

Kaz
fuente
5

Todos los sistemas operativos que merecen el título limpiarán el desorden que hizo su proceso después de la terminación. Pero siempre hay imprevistos, ¿qué pasa si de alguna manera se le negó el acceso y algún mal programador no previó la posibilidad y no vuelve a intentarlo un poco más tarde? Siempre es más seguro limpiar usted mismo SI las fugas de memoria son críticas; de lo contrario, no vale la pena el esfuerzo, en mi opinión, si ese esfuerzo es costoso.

Editar: necesita limpiar las pérdidas de memoria si están en un lugar donde se acumularán, como en bucles. Las pérdidas de memoria de las que hablo son las que se acumulan en un tiempo constante durante el transcurso del programa, si tiene una fuga de cualquier otro tipo, lo más probable es que tarde o temprano sea un problema grave.

En términos técnicos, si sus fugas son de 'complejidad' de memoria O (1) están bien en la mayoría de los casos, O (logn) ya son desagradables (y en algunos casos fatales) y O (N) + intolerables.


fuente
3

La memoria compartida en sistemas que cumplen con POSIX persiste hasta que se llama a shm_unlink o se reinicia el sistema.

kaprender
fuente
2

Si tiene comunicación entre procesos, esto puede llevar a que otros procesos nunca se completen y consuman recursos según el protocolo.

Para dar un ejemplo, una vez estaba experimentando con la impresión en una impresora PDF en Java cuando terminé la JVM en medio de un trabajo de impresora, el proceso de cola de PDF permaneció activo y tuve que eliminarlo en el administrador de tareas antes de poder hacerlo. vuelva a intentar la impresión.

monstruo del trinquete
fuente