En muchos libros y tutoriales, he escuchado la práctica del manejo de la memoria enfatizada y sentí que sucederían algunas cosas misteriosas y terribles si no liberaba la memoria después de terminar 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 básicamente garantiza la limpieza de la mayoría de los recursos (con la excepción de unos pocos) utilizados por un programa después de la terminación del programa. Que incluye memoria de montón, entre varias otras cosas.
Entiendo por qué querrías cerrar un archivo una vez que hayas terminado de usarlo para que esté disponible para el usuario o por qué quieres desconectar un socket conectado a un servidor para ahorrar ancho de banda, pero parece una tontería tiene que micro administrar TODA su memoria utilizada por su 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 trozo de memoria memoria a lo largo de la vida útil de mi programa, ¿es realmente necesario liberarla justo antes de la finalización del programa?
Editar: La pregunta sugerida como duplicado era específica de la familia de sistemas operativos Unix. Su respuesta principal incluso especificaba una herramienta específica para Linux (por ejemplo, Valgrind). Esta pregunta está destinada a cubrir la mayoría de los sistemas operativos no integrados "normales" y por qué es o no una buena práctica liberar memoria que se necesita durante la vida útil de un programa.
fuente
Respuestas:
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 finalice el proceso, puede ser un enfoque 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 explícitamente la memoria asignada 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,
La vida útil de los programas puede cambiar. Quizás su programa sea una pequeña utilidad de línea de comandos hoy en día, 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 memoria no utilizada ya no es una opción, de lo contrario, su programa comenzará a consumir toda la memoria del servidor disponible con el tiempo . Esto significa que tendrá que revisar todo el programa y agregar código de asignación después. Si tiene suerte, esta es una tarea fácil, si no, puede ser tan difícil que hay muchas posibilidades de que pierda un lugar. Y cuando se encuentre en esa situación, deseará haber agregado la opción "gratuita"
En términos más generales, la escritura de asignación y el código de desasignación relacionado siempre cuenta por pares 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 en las que se debe liberar la memoria .
fuente
main
.Liberar memoria al final de la ejecución de un programa es solo una pérdida de tiempo de CPU. Es como ordenar una casa antes de destruirla desde la órbita.
Sin embargo, a veces lo que era un programa de ejecución corta puede convertirse en parte de un programa de ejecución mucho más largo. Entonces liberar cosas se hace necesario. Si esto no se pensó al menos hasta cierto punto, puede implicar una reelaboración sustancial.
Una solución inteligente para esto es "talloc", que le permite realizar una carga de asignaciones de memoria y luego tirarlas todas con una sola llamada.
fuente
free
suele ser mucho más rápido quemalloc
.Puede usar un lenguaje con recolección de basura (como Scheme, Ocaml, Haskell, Common Lisp e incluso Java, Scala, Clojure).
(¡En la mayoría de los lenguajes GC-ed, no hay forma de liberar memoria explícita y manualmente ! A veces, algunos valores pueden finalizarse , por ejemplo, el GC y el sistema de tiempo de ejecución cerrarían un valor de identificador de archivo cuando ese valor sea inalcanzable; pero esto no es seguro, poco confiable, y en su lugar debe cerrar explícitamente sus identificadores de archivo, ya que la finalización nunca está garantizada)
También podría, para su programa codificado en C (o incluso C ++) usar el recolector de basura conservador de Boehm . A continuación, reemplazaría todo
malloc
conGC_malloc
y no se molestaría en hacerfree
clic en ningún puntero. Por supuesto, debe comprender los pros y los contras del uso de GC de Boehm. Lea también el manual de GC .La gestión de memoria es una propiedad global de un programa. De alguna manera (y la vitalidad de algunos datos dados) no es compositiva ni modular, ya que es una propiedad completa del programa.
Por último, como otros señalaron,
free
es una buena práctica hacer explícitamente su zona de memoria C asignada en el montón. Para un programa de juguete que no asigna mucha memoria, incluso podría decidir no tenerfree
memoria (ya que cuando el proceso haya finalizado, sus recursos, incluido su espacio de direcciones virtuales , serían liberados por el sistema operativo).No, no necesitas hacer eso. Y muchos programas del mundo real no se molestan en liberar algo de memoria que se necesita durante toda la vida útil (en particular, el compilador GCC no está liberando parte de su memoria). Sin embargo, cuando lo haga (por ejemplo, no se moleste en
free
extraer una parte particular de datos asignados dinámicamente en C ), será mejor que comente ese hecho para facilitar el trabajo de futuros programadores en el mismo proyecto. Tiendo a recomendar que la cantidad de memoria no liberada permanezca acotada, y usualmente es relativamente pequeña wrt al total de memoria heap usada.Tenga en cuenta que el sistema a
free
menudo no libera memoria al sistema operativo (por ejemplo, llamando a munmap (2) en los sistemas POSIX), pero generalmente marca una zona de memoria como reutilizable en el futuromalloc
. En particular, el espacio de direcciones virtuales (por ejemplo, como se ve/proc/self/maps
en Linux, ver proc (5) ...) podría no reducirsefree
(por lo tanto, las utilidades les gustaps
otop
informan la misma cantidad de memoria utilizada para su proceso).fuente
No es necesario , ya que no podrá ejecutar su programa correctamente si no lo hace. Sin embargo, hay razones por las que puede elegir, si tiene la oportunidad.
Uno de los casos más poderosos con los que me encuentro (una y otra vez) es que alguien escribe un pequeño código de simulación que se ejecuta en su ejecutable. Dicen "nos gustaría que este código se integre en la simulación". Luego les pregunto cómo planean reinicializar entre las carreras de monte-carlo, y me miran sin comprender. "¿Qué quieres decir con reiniciar? ¿Acabas de ejecutar el programa con la nueva configuración?"
A veces, una limpieza limpia facilita mucho el uso de su software. En el caso de muchos ejemplos, presume que nunca necesita limpiar algo, y hace suposiciones sobre cómo puede manejar los datos y su vida útil en torno a esas presunciones. Cuando se muda a un nuevo entorno donde esas presunciones no son válidas, es posible que algoritmos completos ya no funcionen.
Para ver un ejemplo de cuán extrañas pueden ser las cosas, observe cómo los lenguajes administrados se encargan de la finalización al final de los procesos, o cómo C # se ocupa de la detención programática de los dominios de aplicación. Se atan en nudos porque hay suposiciones que caen por las grietas en estos casos extremos.
fuente
Ignorando los idiomas donde no liberas memoria manualmente de todos modos ...
Lo que usted piensa como "un programa" en este momento podría convertirse en una función o método que forma parte de un programa más grande. Y luego esa función podría llamarse varias veces. Y luego, la memoria que debería haber "liberado manualmente" será una pérdida de memoria. Eso es, por supuesto, una decisión judicial.
fuente
Debido a que es muy probable que en algún momento quieras alterar tu programa, quizás integrarlo con otra cosa, ejecutar varias instancias en secuencia o en paralelo. Entonces será necesario liberar manualmente esta memoria, pero ya no recordará las circunstancias y le costará mucho más tiempo volver a comprender su programa.
Haz las cosas mientras tu comprensión sobre ellas todavía es fresca.
Es una pequeña inversión que puede generar grandes ganancias en el futuro.
fuente
No, no es necesario, pero es una buena idea.
Dices que sientes que "sucederían cosas misteriosas y terribles si no liberara memoria después de que termine de usarlo".
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 difícil (por ejemplo, su espacio de direcciones virtuales se agote) o el rendimiento se vuelva 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 mucha habilidad y disciplina para escribir código que no tenga pérdidas. El enfoque recomendado para desarrollar esta habilidad y disciplina es liberar siempre 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 en su mayoría se han cambiado al disco. Cuando comienzas a liberar todo, la mayor parte de tu memoria debe volver a cambiarse a RAM para que se pueda acceder a la información de contabilidad, solo para descartar inmediatamente los datos. ¡Esto puede hacer que el programa tarde unos minutos en salir! Sin mencionar que ejerció mucha presión sobre el disco y la memoria física durante ese tiempo. Si la memoria física es escasa para empezar (tal vez el programa se está cerrando porque otro programa está masticando mucha memoria), entonces es posible que sea necesario intercambiar páginas individuales varias veces cuando sea necesario liberar varios objetos del programa. misma página, pero no se liberan consecutivamente.
Si, en cambio, el programa simplemente aborta, el sistema operativo simplemente descartará toda la memoria que se ha intercambiado al disco, lo cual es casi instantáneo 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 forzará el intercambio de memoria; si tiene que hacer esto, también podría liberar la memoria.
fuente
Las principales razones para limpiar manualmente son: es menos probable que confunda una pérdida de memoria genuina con un bloque de memoria que se limpiará al salir, a veces detectará errores en el asignador solo cuando recorra toda la estructura de datos para desasignar él, y usted tiene un dolor de cabeza mucho menor si alguna vez refactoriza modo que un objeto no necesita para obtener desasignado antes de salir del programa.
Las principales razones para no realizar la limpieza manualmente son: rendimiento, rendimiento, 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 error o seguridad explotar.
Si le importa el rendimiento, siempre desea crear un perfil para descubrir dónde está malgastando todo su tiempo, en lugar de adivinar. Un posible compromiso es envolver su código de limpieza opcional en un bloque condicional, dejarlo al depurar para obtener los beneficios de escribir y depurar el código, y luego, si y solo si ha determinado empíricamente que es demasiado arriba, dígale al compilador que lo omita en su ejecutable final. Y un retraso en el cierre del programa, casi por definición, casi nunca se encuentra en la ruta crítica.
fuente