Cuando sale de una aplicación C, ¿se libera automáticamente la memoria mal ubicada?

92

Digamos que tengo el siguiente código C:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Cuando compilo y ejecuto ese programa en C, es decir, después de asignar algo de espacio en la memoria, ¿la memoria que asigné todavía se asignará (es decir, básicamente ocupará espacio) después de salir de la aplicación y el proceso finalice?

Andreas Grech
fuente
9
es "de buen estilo" limpiar su memoria, no porque pueda ejecutar un sistema operativo que no tiene memoria protegida (que es la sugerencia principal a continuación), sino porque aumenta la probabilidad de que encuentre pérdidas de memoria y mantiene su código esbelto y correcto ...
Matt Joiner

Respuestas:

111

Depende del sistema operativo. La mayoría de los sistemas operativos modernos (y todos los principales) liberarán memoria no liberada por el programa cuando finalice.

Confiar en esto es una mala práctica y es mejor liberarlo explícitamente. El problema no es solo que su código se vea mal. Puede decidir que desea integrar su pequeño programa en uno más grande y de larga duración. Luego, un tiempo después, tendrás que pasar horas rastreando las pérdidas de memoria.
Confiar en una característica de un sistema operativo también hace que el código sea menos portátil.

Yacoby
fuente
16
Una vez encontré win98 en una plataforma integrada, y basándome en esa experiencia, puedo decir que NO libera memoria cuando los programas se cierran.
San Jacinto
8
@Ken Fue un ejemplo. Además, hay una línea entre YAGNI y la codificación descuidada. No liberar recursos lo atraviesa. El principio YAGNI también estaba destinado a aplicarse a funciones, no al código que hace que el programa funcione correctamente. (Y no liberar memoria es un error).
Yacoby
5
+1: lo más importante a tener en cuenta es que la gestión de la memoria es como Yacoby afirma con bastante acierto: "una característica del sistema operativo" . A menos que me equivoque, el lenguaje de programación no define lo que sucede antes o después de la ejecución del programa.
D.Shawley
9
Liberar memoria manualmente lleva más tiempo, más código e introduce la posibilidad de errores (¡dime que nunca has visto un error en el código de desasignación!). No es "descuidado" omitir intencionalmente algo que es peor en todos los sentidos para su caso de uso particular. A menos que o hasta que tenga la intención de ejecutarlo en algún sistema antiguo / pequeño que no puede liberar páginas después de la finalización del proceso, o integrarlo en un programa más grande (YAGNI), me parece una pérdida neta. Sé que le duele el ego de un programador pensar en no limpiarlo usted mismo, pero ¿de qué manera práctica es realmente mejor?
Ken
6
Cualquiera que proponga pérdidas de memoria en SO debería ser despojado de toda reputación e insignias
Ulterior
53

En general, los sistemas operativos modernos de uso general se limpian después de los procesos terminados . Esto es necesario porque la alternativa es que el sistema pierda recursos con el tiempo y requiera reiniciar debido a programas que están mal escritos o simplemente tienen errores que ocurren raramente y que pierden recursos.

Hacer que su programa libere explícitamente sus recursos de todos modos puede ser una buena práctica por varias razones, como:

  • Si tiene recursos adicionales que son el sistema operativo no limpia al salir, como archivos temporales o cualquier tipo de cambio en el estado de un recurso externo, necesitará un código para lidiar con todas esas cosas al salir, y esto a menudo se combina elegantemente con la liberación de memoria.
  • Si su programa comienza a tener una vida útil más larga, entonces no querrá única forma de liberar memoria sea salir. Por ejemplo, es posible que desee convertir su programa en un servidor (demonio) que siga ejecutándose mientras maneja muchas solicitudes de unidades de trabajo individuales, o su programa podría convertirse en una pequeña parte de un programa más grande.

Sin embargo, aquí hay una razón para omitir la liberación de memoria: apagado eficiente . Por ejemplo, suponga que su aplicación contiene una gran memoria caché. Si cuando sale, pasa por toda la estructura de la caché y la libera una pieza a la vez, no tiene ningún propósito útil y desperdicia recursos. Especialmente, considere el caso en el que el sistema operativo ha intercambiado las páginas de memoria que contienen su caché al disco; Al recorrer la estructura y liberarla, recupera todas esas páginas a la vez , desperdicia una cantidad significativa de tiempo y energía sin ningún beneficio real, ¡y posiblemente incluso haga que otros programas del sistema se intercambien!

Como ejemplo relacionado, existen servidores de alto rendimiento que funcionan creando un proceso para cada solicitud y luego haciéndolo salir cuando termina; de esta manera, ni siquiera tienen que rastrear la asignación de memoria , y nunca realizar ninguna liberación o recolección de basura, ya que todo simplemente desaparece en la memoria libre del sistema operativo al final del proceso. (Se puede hacer el mismo tipo de cosas dentro de un proceso utilizando un asignador de memoria personalizado, pero requiere una programación muy cuidadosa; esencialmente, hacer la propia noción de "procesos ligeros" dentro del proceso del sistema operativo).

Kevin Reid
fuente
11

Mis disculpas por publicar tanto tiempo después de la última publicación en este hilo.

Un punto adicional. No todos los programas logran salidas agradables. Los bloqueos y ctrl-C, etc. harán que un programa se cierre de manera incontrolada. Si su sistema operativo no libera su montón, limpia su pila, elimina variables estáticas, etc., eventualmente colapsará su sistema por pérdidas de memoria o algo peor.

Aparte de esto, es interesante que las fallas / fallas en Ubuntu, y sospecho que todos los demás sistemas operativos modernos, tienen problemas con los recursos "manejados". Los sockets, archivos, dispositivos, etc. pueden permanecer "abiertos" cuando un programa finaliza / falla. También es una buena práctica cerrar cualquier cosa con un "identificador" o "descriptor" como parte de su limpieza antes de la salida elegante.

Actualmente estoy desarrollando un programa que usa sockets en gran medida. Cuando me quedo atascado en un bloqueo, tengo que hacer Ctrl-C fuera de él, por lo tanto, encallando mis enchufes. Agregué un std :: vector para recopilar una lista de todos los sockets abiertos y un controlador sigaction que captura sigint y sigterm. El controlador recorre la lista y cierra los sockets. Planeo hacer una rutina de limpieza similar para usar antes de los lanzamientos que conducirán a la terminación prematura.

¿Alguien quiere comentar sobre este diseño?

Wes Miller
fuente
1
Me alegro de que dijeras esto, porque tenía un programa que dejaba recursos de socket y nuestro sistema Ubuntu necesitaba reiniciarse cada dos semanas o la memoria comenzó a agotarse y hay mucha memoria. No estoy seguro de que los recursos del sistema se destruyan si se olvida de limpiarlos.
octopusgrabbus
8
Stackoverflow no es un foro; no hay nada de malo en responder una vieja pregunta. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

Lo que está sucediendo aquí ( en un sistema operativo moderno ) es que su programa se ejecuta dentro de su propio "proceso". Esta es una entidad del sistema operativo que está dotada de su propio espacio de direcciones, descriptores de archivos, etc.malloc llamadas están asignando memoria del "montón" o páginas de memoria no asignadas que están asignadas a su proceso.

Cuando su programa finaliza, como en este ejemplo, todos los recursos asignados a su proceso son simplemente reciclados / destruidos por el sistema operativo. En el caso de la memoria, todas las páginas de memoria que se le asignan simplemente se marcan como "libres" y se reciclan para el uso de otros procesos. Las páginas son un concepto de nivel inferior al que maneja malloc; como resultado, los detalles de malloc / free simplemente se eliminan a medida que se limpia todo.

Es el equivalente moral de, cuando haya terminado de usar su computadora portátil y quiera dársela a un amigo, no se moleste en eliminar individualmente cada archivo. Simplemente formatee el disco duro.

Dicho todo esto, como señalan todos los demás respondedores, confiar en esto no es una buena práctica:

  1. Siempre debe programar para cuidar los recursos, y en C eso también significa memoria. Puede terminar incrustando su código en una biblioteca, o puede terminar ejecutándose mucho más tiempo de lo esperado.
  2. Es posible que algunos sistemas operativos (los más antiguos y quizás algunos integrados modernos) no mantengan límites de proceso tan estrictos, y sus asignaciones pueden afectar los espacios de direcciones de otros.
Ben Zotto
fuente
4

Si. El sistema operativo limpia los recursos. Bueno ... las versiones antiguas de NetWare no lo hicieron.

Editar: Como señaló San Jacinto, ciertamente hay sistemas (aparte de NetWare) que no hacen eso. Incluso en los programas desechables, trato de tener el hábito de liberar todos los recursos solo para mantener el hábito.

Mark Wilkins
fuente
3
No estoy votando negativamente, pero esta es una publicación bastante peligrosa para la posteridad. DOS todavía se usa en muchas plataformas integradas, y SERIAMENTE dudo que haga la limpieza de memoria por usted. La amplia generalización es incorrecta.
San Jacinto
@San Jacinto: Ese es un buen punto. Es por eso que hice la referencia de NetWare, pero probablemente necesite una aclaración. Lo editaré un poco.
Mark Wilkins
3
@San DOS no es un sistema operativo multitarea: cuando finaliza un programa de DOS (excluyendo los TSR), toda la memoria está disponible para que se cargue el siguiente programa.
@Neil, gracias por el recordatorio, pero me refería a un programa similar a TSR que se lanzaría cuando ocurre un evento, como es un uso común para los sistemas integrados. No obstante, gracias por su experiencia y aclaración donde fallé :)
San Jacinto
2

Sí, el sistema operativo libera toda la memoria cuando finaliza el proceso.

Draemon
fuente
No veo por qué fue rechazado. La memoria malloc'ed se liberará cuando el proceso muera (la definición de malloc de Wikipedia lo dice)
Arve
7
Wikipedia no es el manual para todos los sistemas operativos existentes. La mayoría de los sistemas operativos modernos recuperarán la memoria, pero no todos (y especialmente no todos los antiguos) lo hacen. Agregue a eso, mallocsolo puedo prometer lo que C hará con la memoria; por diseño, C no garantiza mucho de nada con respecto al comportamiento fuera de C mismo. Si la aplicación muere inesperadamente, cualquier promesa hecha por la biblioteca en tiempo de ejecución es nula y sin efecto, ya que ya no está viva para cumplirla.
cHao
2

Depende, los sistemas operativos generalmente lo limpiarán por usted, pero si está trabajando, por ejemplo, en software integrado, es posible que no se publique.

Solo asegúrese de liberarlo, puede ahorrarle mucho tiempo más adelante, cuando desee integrarlo en un proyecto grande.

Charles
fuente
0

Eso realmente depende del sistema operativo, pero para todos los sistemas operativos que encontrará, la asignación de memoria desaparecerá cuando finalice el proceso.


fuente
0

Creo que la liberación directa es lo mejor. El comportamiento indefinido es lo peor, por lo que si tiene acceso mientras aún está definido en su proceso, hágalo, hay muchas buenas razones que la gente ha dado para ello.

En cuanto a dónde, o si, encontré eso en W98, la verdadera pregunta era 'cuándo' (no vi una publicación que enfatice esto). Un pequeño programa de plantilla (para entrada MIDI SysEx, usando varios espacios malloc'd) liberaría memoria en el bit WM_DESTROY del WndProc, pero cuando trasplante esto a un programa más grande, se bloqueó al salir. Supuse que esto significaba que estaba tratando de liberar lo que el sistema operativo ya había liberado durante una limpieza más grande. Si lo hice en WM_CLOSE, luego llamé a DestroyWindow (), todo funcionó bien, salida limpia instantánea.

Si bien esto no es exactamente lo mismo que los búferes MIDI, hay una similitud en que es mejor mantener el proceso intacto, limpiar completamente y luego salir. Con trozos de memoria modestos, esto es muy rápido. Descubrí que muchos búferes pequeños funcionaban más rápido en operación y limpieza que pocos grandes.

Pueden existir excepciones, como dijo alguien al evitar extraer grandes trozos de memoria de un archivo de intercambio en el disco, pero incluso eso puede minimizarse manteniendo más espacios asignados y más pequeños.


fuente