Cuando sale de una aplicación C, ¿la memoria malloc-ed se libera automáticamente?

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 progtwig C, es decir, después de asignar algún espacio en la memoria, ¿esa memoria que asigné todavía se asignará (es decir, ocupará espacio) después de salir de la aplicación y el proceso finalizará?

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

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

En general, los sistemas operativos modernos de uso general limpian después de los procesos finalizados . Esto es necesario porque la alternativa es que el sistema pierda recursos a lo largo del tiempo y requiera reiniciarse debido a progtwigs que están mal escritos o simplemente tienen errores que ocurren raramente y que filtran recursos.

Tener su progtwig de manera explícita liberar sus recursos de todos modos puede ser una buena práctica por varias razones, tales como:

  • Si tiene recursos adicionales que el SO no limpia al salir, como archivos temporales o cualquier tipo de cambio en el estado de un recurso externo, entonces necesitará un código para manejar todos estos elementos al salir, y esto a menudo se combina elegantemente con la liberación de memoria.
  • Si su progtwig comienza a tener una vida útil más larga, entonces no querrá que la única forma de liberar memoria sea salir. Por ejemplo, es posible que desee convertir su progtwig en un servidor (daemon) que se ejecuta mientras maneja muchas solicitudes de unidades de trabajo individuales, o su progtwig puede convertirse en una pequeña parte de un progtwig más grande.

Sin embargo, aquí hay una razón para omitir la liberación de memoria: cierre eficiente . Por ejemplo, supongamos que su aplicación contiene un gran caché en la memoria. Si cuando sale atraviesa toda la estructura de caché y la libera de a una por vez, eso no sirve para nada y desperdicia recursos. Especialmente, considere el caso donde el sistema operativo ha intercambiado las páginas de memoria que contienen su memoria caché en disco; al caminar por la estructura y liberarla, está devolviendo todas esas páginas a la memoria de una sola vez , desperdiciando tiempo y energía significativos sin ningún beneficio real, y posiblemente incluso causando que otros progtwigs en el sistema se canjeen.

Como ejemplo relacionado, hay servidores de alto rendimiento que funcionan al crear un proceso para cada solicitud, luego hacer que finalice cuando termine; de esta forma, ni siquiera tienen que rastrear la asignación de memoria, y nunca realizan ninguna liberación o recolección de basura, ya que todo simplemente desaparece en la memoria libre del sistema operativo al final del proceso. (El mismo tipo de cosas se puede hacer dentro de un proceso usando un asignador de memoria personalizado, pero requiere una progtwigción muy cuidadosa, esencialmente haciendo su propia noción de “procesos livianos” dentro del proceso del sistema operativo).

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

Un punto adicional. No todos los progtwigs llegan a salidas elegantes. Bloqueos y Ctrl-C, etc. harán que un progtwig salga de forma no controlada. Si su sistema operativo no liberó su stack, no limpió su stack, borró variables estáticas, etc., eventualmente se colgaría su sistema de pérdidas de memoria o algo peor.

Interesante aparte de esto, los lockings / fallas en Ubuntu, y sospecho que todos los otros sistemas operativos modernos, tienen problemas con los recursos “manejados”. Los sockets, archivos, dispositivos, etc. pueden permanecer “abiertos” cuando un progtwig finaliza / falla. También es una buena práctica cerrar cualquier cosa con un “mango” o “descriptor” como parte de su limpieza antes de salir con elegancia.

Actualmente estoy desarrollando un progtwig que usa sockets en gran medida. Cuando me quedo atrapado en un hang, tengo que sacarlo de allí, así, bloqueando mis sockets. Agregué un std :: vector para recostackr una lista de todos los sockets abiertos y un manejador sigaction que captura sigint y sigterm. El manejador recorre la lista y cierra los sockets. Planeo hacer una rutina de limpieza similar para usar antes de los lanzamientos que dará lugar a la terminación prematura.

¿Alguien quiere comentar sobre este diseño?

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

Cuando finaliza su progtwig, como en este ejemplo, el sistema operativo simplemente recicla / destruye todos los recursos asignados a su proceso. En el caso de la memoria, todas las páginas de memoria que tiene asignadas 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 malloc maneja; como resultado, las características específicas de malloc / free simplemente se eliminan cuando todo se limpia.

Es el equivalente moral de, cuando termines de usar tu computadora portátil y quieras dársela a un amigo, no te molestes en borrar cada archivo por separado. Usted acaba de formatear el disco duro.

Dicho todo esto, como todos los demás contendientes están notando, confiar en esto no es una buena práctica:

  1. Siempre debe progtwigr para cuidar los recursos, y en C también significa memoria. Es posible que termine incrustando su código en una biblioteca, o podría terminar ejecutándose mucho más tiempo del esperado.
  2. Algunos sistemas operativos (los más antiguos y tal vez algunos integrados modernos) pueden no mantener esos límites de proceso difíciles, y sus asignaciones pueden afectar los espacios de direcciones de los demás.

Sí. 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 progtwigs desechables, bash crear el hábito de liberar todos los recursos solo para mantener el hábito.

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

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

Solo asegúrate de liberarlo, puede ahorrarte mucho tiempo más tarde cuando quieras integrarlo en un proyecto grande.

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

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

En cuanto a dónde, o si, encontré eso en W98, la verdadera pregunta era ‘cuándo’ (no vi una publicación que hiciera hincapié en esto). Un pequeño progtwig de plantilla (para la entrada MIDI SysEx, usando varios espacios malloc) liberaría memoria en el bit WM_DESTROY del WndProc, pero cuando lo transplanté a un progtwig 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 de limpieza instantánea.

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

Pueden existir excepciones, como dijo alguien cuando se evita transportar trozos grandes de memoria fuera de un archivo de intercambio en el disco, pero incluso eso se puede minimizar manteniendo más espacios asignados y más pequeños.