Si necesito usar un trozo de memoria durante toda la vida útil de mi programa, ¿es realmente necesario liberarlo justo antes de que finalice el programa?

65

En muchos libros y tutoriales, escuché que la práctica del manejo de la memoria estaba estresada y sentí que sucederían cosas misteriosas y terribles si no liberaba la memoria una vez que hubiera terminado de usarla.

No puedo hablar por otros sistemas (aunque para mí es razonable suponer que adoptan una práctica similar), pero al menos en Windows, el Kernel está básicamente garantizado para limpiar la mayoría de los recursos (con la excepción de unos pocos ) utilizado por un programa después de la terminación del programa. Que incluye memoria de pila, entre varias otras cosas.

Entiendo por qué querría cerrar un archivo después de que haya terminado de usarlo para ponerlo a disposición del usuario o por qué querría desconectar un zócalo conectado a un servidor para ahorrar ancho de banda, pero Parece tonto tener que microgestionar TODA la memoria utilizada por tu programa.

Ahora, estoy de acuerdo en que esta pregunta es amplia, ya que la forma en que debe manejar su memoria se basa en la cantidad de memoria que necesita y cuándo la necesita, por lo que limitaré el alcance de esta pregunta a esto: Si ¿Necesito usar un pedazo de memoria durante toda la vida útil de mi programa? ¿Es realmente necesario liberarlo justo antes de que termine el programa?

Editar: La pregunta sugerida como duplicado era específica de la familia de sistemas operativos Unix. Su respuesta principal incluso especificó una herramienta específica para Linux (por ejemplo, Valgrind). El objetivo de esta pregunta es cubrir la mayoría de los sistemas operativos no incorporados "normales" y por qué es o no es una buena práctica liberar memoria que se necesita durante toda la vida útil de un programa.

    
pregunta CaptainObvious 27.12.2015 - 08:36

8 respuestas

108
  

Si necesito usar un pedazo de memoria durante toda la vida útil de mi programa, ¿es realmente necesario liberarlo justo antes de que finalice el programa?

No es obligatorio, pero puede tener beneficios (así como algunos inconvenientes).

Si el programa asigna memoria una vez durante su tiempo de ejecución, y de lo contrario nunca la liberaría hasta que el proceso finalice, puede ser un método sensato no liberar la memoria manualmente y confiar en el sistema operativo. En todos los sistemas operativos modernos que conozco, esto es seguro, al final del proceso, toda la memoria asignada se devuelve de manera confiable al sistema.
En algunos casos, no limpiar la memoria asignada explícitamente puede ser notablemente más rápido que hacer la limpieza.

Sin embargo, al liberar explícitamente toda la memoria al final de la ejecución,

  • durante la depuración / prueba, las herramientas de detección de fugas de mem no mostrarán "falsos positivos"
  • podría ser mucho más fácil mover el código que usa la memoria junto con la asignación y la desasignación a un componente separado y luego usarlo en un contexto diferente donde el tiempo del uso de la memoria debe ser controlado por el usuario del componente

La vida útil de los programas puede cambiar. Tal vez su programa sea una pequeña utilidad de línea de comandos hoy, con una vida útil típica de menos de 10 minutos, y asigne memoria en porciones de algunos kb cada 10 segundos, por lo que no es necesario liberar ninguna memoria asignada antes de que finalice el programa. Más adelante, el programa cambia y obtiene un uso extendido como parte de un proceso de servidor con una vida útil de varias semanas, por lo que no liberar más la memoria no utilizada en el medio ya no es una opción, de lo contrario, su programa comienza a consumir toda la memoria del servidor disponible con el tiempo . Esto significa que tendrá que revisar todo el programa y agregar el código de desasignación posteriormente. Si tiene suerte, esta es una tarea fácil, si no, puede ser tan difícil que las posibilidades son altas que se pierde un lugar. Y cuando se encuentre en esa situación, deseará haber agregado el código "gratis" a su programa de antemano, en el momento en que agregó el código "malloc".

Más generalmente, la asignación de códigos de desasignación y asignaciones relacionadas siempre se contabiliza como un "buen hábito" entre muchos programadores: al hacer esto siempre, disminuye la probabilidad de olvidar el código de desasignación en situaciones donde la memoria debe ser liberado.

    
respondido por el Doc Brown 27.12.2015 - 09:36
11

Liberar memoria al final de una ejecución de programas es solo una pérdida de tiempo de CPU. Es como ordenar una casa antes de matarla desde la órbita.

Sin embargo, a veces, lo que era un programa de ejecución corta puede convertirse en parte de una ejecución mucho más larga. Entonces la liberación de cosas se vuelve necesaria. Si esto no se pensó al menos en cierta medida, puede implicar una modificación sustancial.

Una solución inteligente para esto es "talloc", que te permite realizar una carga de asignaciones de memoria y luego deshacerte de ellas con una sola llamada.

    
respondido por el Peter Green 27.12.2015 - 16:10
5

Puede usar un idioma con recolección de basura (como Scheme, Ocaml, Haskell, Common Lisp , e incluso Java, Scala, Clojure).

(En la mayoría de los idiomas editados por GC, no hay ninguna manera para explícitamente y manualmente memoria libre. A veces, algunos valores podría estar finalizado , por ejemplo, el GC y el runtime system cerraría un valor de identificador de archivo cuando ese valor no esté disponible; pero esto no es seguro, no es confiable, y en su lugar debería cerrar explícitamente sus identificadores de archivo, ya que la finalización nunca es garantizado)

También puede, para su programa codificado en C (o incluso C ++) utilizar el recolector de basura conservador de Boehm . Luego, reemplazarías todos tus malloc con GC_malloc y no te preocuparías por free -ing ningún puntero. Por supuesto, debe comprender las ventajas y desventajas de usar el GC de Boehm. Lea también el manual de GC .

La administración de memoria es una propiedad global de un programa. De alguna manera, (y la vida de algunos datos dados) es no compositivo y no modular, ya que es una propiedad completa del programa.

Por fin, como señalan otros, explícitamente free -ing su zona de memoria C asignada al montón es una buena práctica. Para un programa de juguete que no asigna mucha memoria, incluso podría decidir no free memory (ya que cuando el proceso ha finalizado, sus recursos, incluido su espacio de direcciones virtuales , serían liberados por el sistema operativo) .

  

Si necesito usar un pedazo de memoria durante toda la vida útil de mi programa, ¿es realmente necesario liberarlo justo antes de que finalice el programa?

No, no necesitas hacer eso. Y muchos programas del mundo real no se molestan en liberar algo de memoria que se necesita en toda la vida útil (en particular, el compilador GCC no libera a algunos de su memoria). Sin embargo, cuando haces eso (por ejemplo, no molestas a free -ing una pieza particular de C asignada dinámicamente datos ), será mejor que comente ese hecho para facilitar el trabajo de los futuros programadores en el mismo proyecto. Tiendo a recomendar que la cantidad de memoria no liberada permanezca acotada y, por lo general, relativamente pequeña. a la memoria total del montón utilizado.

Observe que el sistema free a menudo no libera memoria para el sistema operativo (por ejemplo, llamando a munmap (2) en sistemas POSIX), pero generalmente se marca una zona de memoria como reutilizable por el futuro malloc . En particular, el espacio de direcciones virtuales (por ejemplo, como se ve a través de /proc/self/maps en Linux, consulte proc (5 ) ....) puede que no se reduzca después de free (por lo tanto, las utilidades como ps o top reportan la misma cantidad de memoria utilizada para su proceso).

    
respondido por el Basile Starynkevitch 27.12.2015 - 11:32
3

No es necesario , ya que no fallará para ejecutar su programa correctamente si no lo hace. Sin embargo, hay razones que puede elegir, si se le da una oportunidad.

Uno de los casos más poderosos con los que me encuentro (una y otra vez) es que alguien escribe un pequeño fragmento de código de simulación que se ejecuta en su ejecutable. Dicen que "nos gustaría que este código se integrara en la simulación". Luego les pregunto cómo planean reiniciarse entre las carreras de monte-carlo, y me miran fijamente. "¿Qué quiere decir con reiniciar? ¿Acaba de ejecutar el programa con nuevas configuraciones?"

A veces, una limpieza limpia hace que sea mucho más fácil para su software. En el caso de muchos ejemplos, usted presume que nunca necesita limpiar algo, y hace suposiciones acerca de cómo puede manejar los datos y su vida útil en torno a esas presunciones. Cuando te mudas a un nuevo entorno donde esas presunciones no son válidas, es posible que los algoritmos completos ya no funcionen.

Para ver un ejemplo de cuán extrañas pueden llegar a ser las cosas, observe cómo los idiomas administrados manejan la finalización al final de los procesos, o cómo C # se ocupa de la detención programática de los dominios de aplicaciones. Se atan en nudos porque hay suposiciones que caen a través de las grietas en estos casos extremos.

    
respondido por el Cort Ammon 27.12.2015 - 21:55
1

De todos modos, ignora los idiomas en los que no liberas la memoria manualmente ...

Lo que piensas como "un programa" en este momento podría convertirse en algún momento en una función o método que sea parte de un programa más grande. Y entonces esa función podría ser llamada varias veces. Y luego la memoria que debería haber "liberado manualmente" será una pérdida de memoria. Eso es, por supuesto, una llamada de juicio.

    
respondido por el gnasher729 27.12.2015 - 14:33
1

Porque es muy probable que en algún momento desees alterar tu programa, tal vez integrarlo con otra cosa, ejecutar varias instancias en secuencia o en paralelo. Entonces será necesario liberar manualmente esta memoria, pero ya no recordarás las circunstancias y te costará mucho más tiempo volver a entender tu programa.

Haz las cosas mientras tu comprensión acerca de ellas todavía está fresca.

Es una pequeña inversión que puede generar grandes retornos en el futuro.

    
respondido por el Agent_L 28.12.2015 - 11:05
1

No, no es necesario, pero es una buena idea.

Dices que sientes que "sucederían cosas misteriosas y terribles si no liberara la memoria después de que termine de usarla".

En términos técnicos, la única consecuencia de esto es que su programa seguirá consumiendo más memoria hasta que se alcance un límite estricto (por ejemplo, se agote su espacio de direcciones virtuales) o el rendimiento sea inaceptable. Si el programa está a punto de salir, nada de esto importa porque el proceso efectivamente deja de existir. Las "cosas misteriosas y terribles" son puramente sobre el estado mental del desarrollador. Encontrar la fuente de una pérdida de memoria puede ser una pesadilla absoluta (esto es un eufemismo) y se necesita un lote de habilidad y disciplina para escribir código sin fugas. El enfoque recomendado para desarrollar esta habilidad y disciplina es siempre liberar la memoria una vez que ya no sea necesaria, incluso si el programa está a punto de finalizar.

Por supuesto, esto tiene la ventaja adicional de que su código puede reutilizarse y adaptarse más fácilmente, como han dicho otros.

Sin embargo , hay al menos un caso en el que es mejor no liberar memoria justo antes de la finalización del programa.

Considere el caso en el que ha realizado millones de pequeñas asignaciones, y la mayoría de ellas se han cambiado a disco. Cuando comienza a liberar todo, la mayoría de su memoria debe volver a cambiarse a la RAM para que se pueda acceder a la información de la contabilidad, solo para descartar inmediatamente los datos. ¡Esto puede hacer que el programa tarde unos minutos en salir! Por no hablar de poner mucha presión en el disco y en la memoria física durante ese tiempo. Si para empezar la memoria física es escasa (quizás el programa se está cerrando porque otro programa está consumiendo mucha memoria), entonces es posible que sea necesario intercambiar y extraer páginas individuales varias veces cuando se deben liberar varios objetos de la memoria. misma página, pero no se liberan consecutivamente.

Si, por el contrario, el programa simplemente se interrumpe, el sistema operativo simplemente descartará toda la memoria que se ha intercambiado en el disco, que es casi instantánea porque no requiere ningún acceso al disco.

Es importante tener en cuenta que en un lenguaje OO, llamar al destructor de un objeto también obligará a la memoria a intercambiarse; Si tiene que hacer esto, también puede liberar la memoria.

    
respondido por el Artelius 28.12.2015 - 06:17
0

Las razones principales para limpiar manualmente son: es menos probable que confunda una pérdida de memoria genuina con un bloque de memoria que solo se limpiará en la salida, a veces se detectan errores en el asignador solo cuando recorre todo el camino. estructura de datos para desasignarla, y tendrá un dolor de cabeza mucho menor si alguna vez se refactoriza, de modo que algún objeto deba ser desasignado antes de que el programa salga.

Las razones principales para no limpiar manualmente son: el rendimiento, el rendimiento, el rendimiento y la posibilidad de algún tipo de error free-two o use-after-free en su código de limpieza innecesario, que puede convertirse en un fallo bug o exploit de seguridad.

Si te importa el rendimiento, siempre quieres hacer un perfil para saber dónde estás perdiendo todo el tiempo, en lugar de adivinar. Un posible compromiso es envolver su código de limpieza opcional en un bloque condicional, dejarlo en la depuración para que obtenga los beneficios de escribir y depurar el código, y luego, solo si ha determinado empíricamente que es demasiado encima, dile al compilador que lo omita en tu ejecutable final. Y un retraso en el cierre del programa es, casi por definición, casi nunca en la ruta crítica.

    
respondido por el Davislor 28.12.2015 - 04:16

Lea otras preguntas en las etiquetas