¿Qué pasa REALMENTE cuando no liberas después de Malloc?

538

Esto ha sido algo que me ha molestado por años.

A todos nos enseñan en la escuela (al menos, yo era) que DEBES liberar cada puntero asignado. Sin embargo, tengo un poco de curiosidad sobre el costo real de no liberar memoria. En algunos casos obvios, como cuando mallocse llama dentro de un bucle o parte de una ejecución de subproceso, es muy importante liberar para que no haya pérdidas de memoria. Pero considere los siguientes dos ejemplos:

Primero, si tengo código, es algo como esto:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

¿Cuál es el resultado real aquí? Mi opinión es que el proceso muere y luego el espacio de almacenamiento dinámico desaparece de todos modos, por lo que no hay nada de malo en perder la llamada free(sin embargo, reconozco la importancia de tenerlo de todos modos para el cierre, la mantenibilidad y las buenas prácticas). ¿Estoy en lo cierto en este pensamiento?

Segundo, digamos que tengo un programa que actúa un poco como un shell. Los usuarios pueden declarar variables como aaa = 123y se almacenan en alguna estructura de datos dinámicos para su uso posterior. Claramente, parece obvio que usaría alguna solución que invocará alguna función * alloc (hashmap, lista vinculada, algo así). Para este tipo de programa, no tiene sentido liberarse nunca después de llamar mallocporque estas variables deben estar presentes en todo momento durante la ejecución del programa y no hay una buena manera (que pueda ver) de implementar esto con espacio estáticamente asignado. ¿Es un mal diseño tener un montón de memoria asignada pero solo liberada como parte del final del proceso? Si es así, ¿cuál es la alternativa?

Scott
fuente
77
@NTDLS La magia del sistema de calificación realmente funciona por una vez: 6 años después y la respuesta "más merecedora" ha llegado a la cima.
zxq9
15
Las personas a continuación siguen diciendo que un buen sistema operativo moderno hace la limpieza, pero ¿qué pasa si el código se ejecuta en modo kernel (por ejemplo, por razones de rendimiento)? ¿Los programas en modo kernel (en Linux, por ejemplo) están protegidos? Si no es así, creo que necesitaría liberar manualmente todo, supongo, incluso antes de cualquier terminación anormal como con abort ().
Dr. Person Person II
3
@ Dr.PersonPersonII Sí, el código que se ejecuta en modo kernel generalmente tiene que liberar todo manualmente.
zwol
1
¡Me gustaría agregar que en free(a)realidad no hace nada para liberar memoria! Simplemente restablece algunos punteros en la implementación libc de malloc que realizan un seguimiento de los fragmentos de memoria disponibles dentro de una gran página de memoria mmapped (comúnmente llamada el "montón"). Esa página solo se liberará cuando finalice su programa, no antes.
Marco Bonelli
1
Free () podría o no liberar la memoria. Podría simplemente marcar el bloque como liberado, para ser reclamado más tarde, o podría vincularlo a una lista libre. Podría fusionarlo en bloques libres adyacentes, o podría dejarlo para que lo haga una asignación posterior. Todo es un detalle de implementación.
Jordan Brown

Respuestas:

378

Casi todos los sistemas operativos modernos recuperarán todo el espacio de memoria asignado después de que un programa salga. La única excepción que se me ocurre podría ser algo así como Palm OS, donde el almacenamiento estático del programa y la memoria de tiempo de ejecución son más o menos lo mismo, por lo que no liberarlo podría hacer que el programa tome más espacio de almacenamiento. (Solo estoy especulando aquí).

Por lo tanto, en general, no hay daño, excepto el costo de tiempo de ejecución de tener más almacenamiento del que necesita. Ciertamente, en el ejemplo que da, desea mantener la memoria para una variable que podría usarse hasta que se borre.

Sin embargo, se considera un buen estilo liberar memoria tan pronto como ya no la necesite, y liberar todo lo que todavía tiene al salir del programa. Es más un ejercicio para saber qué memoria estás usando y pensar si aún la necesitas. Si no realiza un seguimiento, es posible que tenga pérdidas de memoria.

Por otro lado, la advertencia similar para cerrar sus archivos al salir tiene un resultado mucho más concreto: si no lo hace, los datos que les escribió podrían no vaciarse, o si son un archivo temporal, podrían no ser eliminado cuando hayas terminado. Además, los identificadores de la base de datos deben tener sus transacciones confirmadas y luego cerrarse cuando haya terminado con ellas. Del mismo modo, si está utilizando un lenguaje orientado a objetos como C ++ u Objective C, no liberar un objeto cuando haya terminado significará que nunca se llamará al destructor, y los recursos de los que es responsable la clase podrían no limpiarse.

Paul Tomblin
fuente
16
Probablemente también sería bueno mencionar que no todos están usando un sistema operativo moderno, si alguien toma su programa (y todavía se ejecuta en un sistema operativo que no recupera memoria) lo ejecuta luego GG.
user105033
79
Realmente considero que esta respuesta es incorrecta. Uno siempre debe desasignar recursos después de que uno haya terminado con ellos, ya sea manejadores de archivos / memoria / mutexs. Al tener ese hábito, uno no cometerá ese tipo de error al construir servidores. Se espera que algunos servidores funcionen 24x7. En esos casos, cualquier fuga de cualquier tipo significa que su servidor finalmente se quedará sin ese recurso y se bloqueará / bloqueará de alguna manera. Un programa de utilidad corto, una fuga no es tan malo. Cualquier servidor, cualquier fuga es la muerte. Hazte un favor. Limpia después de ti mismo. Es un buen habito.
EvilTeach
120
Qué parte de "Sin embargo, se considera un buen estilo liberar memoria tan pronto como ya no la necesite, y liberar todo lo que todavía tiene al salir del programa". ¿te parece mal entonces?
Paul Tomblin
24
Si tiene un almacén de memoria que necesita hasta el momento en que se cierra el programa, y ​​no se está ejecutando en un sistema operativo primitivo, liberar la memoria justo antes de salir es una opción estilística, no un defecto.
Paul Tomblin
30
@Paul - Simplemente de acuerdo con EvilTeach, no se considera un buen estilo liberar memoria, es incorrecto no liberar memoria. Su redacción hace que esto parezca tan importante como usar un pañuelo que combine con su corbata. En realidad, está en el nivel de usar pantalones.
Heath Hunnicutt
110

Sí, tiene razón, su ejemplo no hace ningún daño (al menos no en la mayoría de los sistemas operativos modernos). Toda la memoria asignada por su proceso será recuperada por el sistema operativo una vez que el proceso finalice.

Fuente: Mitos de asignación y GC (¡alerta de PostScript!)

Mito de asignación 4: los programas no recolectados de basura siempre deben desasignar toda la memoria que asignan.

La verdad: las desasignaciones omitidas en el código ejecutado con frecuencia causan fugas cada vez mayores. Raramente son aceptables. pero los programas que retienen la memoria más asignada hasta la salida del programa a menudo funcionan mejor sin ninguna desasignación intermedia. Malloc es mucho más fácil de implementar si no hay gratis.

En la mayoría de los casos, desasignar memoria justo antes de la salida del programa no tiene sentido. El sistema operativo lo reclamará de todos modos. El libre albedrío toca y pagina en los objetos muertos; el sistema operativo no lo hará.

Consecuencia: Tenga cuidado con los "detectores de fugas" que cuentan las asignaciones. ¡Algunas "fugas" son buenas!

Dicho esto, ¡realmente deberías tratar de evitar todas las pérdidas de memoria!

Segunda pregunta: tu diseño está bien. Si necesita almacenar algo hasta que su aplicación salga, entonces está bien hacerlo con asignación de memoria dinámica. Si no conoce el tamaño requerido por adelantado, no puede usar memoria asignada estáticamente.

compie
fuente
3
Podría ser porque la pregunta, tal como la leí, es qué está sucediendo realmente con la memoria filtrada, no si este ejemplo específico está bien. Sin embargo, no lo rechazaría, porque sigue siendo una buena respuesta.
DevinB
3
Probablemente hubo (sistemas operativos Windows tempranos, Mac OS tempranos), y tal vez todavía lo son, sistemas operativos que requieren procesos para liberar memoria antes de salir, de lo contrario no se recupera el espacio.
Pete Kirkham
Está perfectamente bien, a menos que le importe la fragmentación de la memoria o la falta de memoria; lo hace demasiado y el rendimiento de sus aplicaciones desaparecerá. Además de los hechos concretos, siga siempre las mejores prácticas y la creación de buenos hábitos.
NTDLS
1
Tengo una respuesta aceptada que actualmente está en torno a -11, por lo que ni siquiera está en la carrera por el récord.
Paul Tomblin
8
Creo que es incorrecto explicar la necesidad de liberar () la memoria diciendo "debido al detector de fugas". Esto es como decir "tienes que conducir despacio en una calle de juegos porque los policías podrían estar esperándote con una cámara de velocidad".
Sebastian Mach
57

=== ¿Qué pasa con las pruebas futuras y la reutilización de código ? ===

Si no escribe el código para liberar los objetos, entonces está limitando el código para que solo sea seguro de usar cuando puede depender de que la memoria se libere al cerrar el proceso ... es decir, un pequeño uso de una sola vez proyectos o proyectos "desechables" [1] ) ... donde se sabe cuándo terminará el proceso.

Si haces escribir el código que el libre () s de toda la memoria asignada dinámicamente, entonces usted está a prueba de futuro el código y dejar que otros lo utilizan en un proyecto más amplio.


[1] con respecto a proyectos "desechables". El código utilizado en proyectos "desechables" tiene una forma de no desecharse. Lo siguiente que sabes es que han pasado diez años y tu código de "descarte" todavía se está usando).

Escuché una historia sobre un tipo que escribió un código solo por diversión para hacer que su hardware funcione mejor. Él dijo " solo un hobby, no será grande y profesional ". Años más tarde, muchas personas están usando su código de "pasatiempo".

Trevor Boyd Smith
fuente
8
Votado a favor de "pequeños proyectos". Hay muchos proyectos grandes que intencionalmente no liberan memoria al salir porque es una pérdida de tiempo si conoce sus plataformas de destino. OMI, un ejemplo más preciso habría sido "proyectos aislados". Por ejemplo, si está creando una biblioteca reutilizable que se incluirá en otras aplicaciones, no hay un punto de salida bien definido, por lo que no debería perder memoria. Para una aplicación independiente, siempre sabrá exactamente cuándo finaliza el proceso y puede tomar una decisión consciente de descargar la limpieza en el sistema operativo (que tiene que hacer las comprobaciones de cualquier manera).
Dan Bechard
La aplicación de ayer es la función de biblioteca de hoy, y mañana estará vinculada a un servidor de larga duración que lo llama miles de veces.
Adrian McCarthy
1
@AdrianMcCarthy: si una función comprueba si un puntero estático es nulo, lo inicializa con malloc()si es así y termina si el puntero sigue siendo nulo, dicha función se puede usar de forma segura un número arbitrario de veces, incluso si freenunca se llama. Creo que probablemente valga la pena distinguir las pérdidas de memoria que pueden consumir una cantidad ilimitada de almacenamiento, frente a situaciones que solo pueden desperdiciar una cantidad finita y predecible de almacenamiento.
supercat
@supercat: Mi comentario hablaba sobre el cambio de código con el tiempo. Claro, perder una cantidad limitada de memoria no es un problema. Pero algún día, alguien querrá cambiar esa función para que ya no use un puntero estático. Si el código no tiene ninguna disposición para poder liberar la memoria apuntada, será un cambio difícil (o, lo que es peor, el cambio será malo y terminará con una fuga ilimitada).
Adrian McCarthy
1
@AdrianMcCarthy: Cambiar el código para que ya no use un puntero estático probablemente requeriría mover el puntero a algún tipo de objeto "contextual" y agregar código para crear y destruir dichos objetos. Si el puntero siempre es nullsi no existe una asignación, y no es nulo cuando existe una asignación, tener un código libre de la asignación y establecer el puntero para nullcuando se destruye un contexto sería sencillo, especialmente en comparación con todo lo demás que debería hacerse para mover objetos estáticos a una estructura de contexto.
supercat
52

Estás en lo correcto, no se hace daño y es más rápido salir

Hay varias razones para esto:

  • Todos los entornos de escritorio y servidor simplemente liberan todo el espacio de memoria al salir (). No conocen las estructuras de datos internas del programa, como los montones.

  • Casi todas las free()implementaciones nunca devuelven memoria al sistema operativo de todos modos.

  • Más importante aún, es una pérdida de tiempo cuando se hace justo antes de la salida (). Al salir, las páginas de memoria y el espacio de intercambio simplemente se liberan. Por el contrario, una serie de llamadas gratuitas () quemarán el tiempo de CPU y pueden provocar operaciones de paginación de disco, fallas de caché y desalojos de caché.

En cuanto a la posibilidad de reutilización de código futuro justificando la certeza de operaciones sin sentido: eso es una consideración, pero posiblemente no sea la forma ágil . YAGNI!

DigitalRoss
fuente
2
Una vez trabajé en un proyecto en el que pasamos poco tiempo tratando de entender el uso de memoria de un programa (se nos exigió que lo admitiéramos, no lo escribimos). Basado en la experiencia, estoy anecdóticamente de acuerdo con su segunda viñeta. Sin embargo, me gustaría escuchar que usted (o alguien) proporcione más pruebas de que esto es cierto.
user106740
3
No importa, encontré la respuesta: stackoverflow.com/questions/1421491/… . Muchas gracias!
user106740
@aviggiano que se llama YAGNI.
v.oddou
El principio YAGNI funciona en ambos sentidos: nunca necesitará optimizar la ruta de cierre. Optimizaciones prematuras y todo eso.
Adrian McCarthy
26

Estoy completamente en desacuerdo con todos los que dicen que OP es correcto o no hay daño.

Todos están hablando de un sistema operativo moderno y / o heredado.

Pero, ¿qué pasa si estoy en un entorno donde simplemente no tengo SO? Donde no hay nada?

Imagínese ahora que está utilizando interrupciones con estilo de hilo y asigna memoria. En el estándar C ISO / IEC: 9899 es la vida útil de la memoria indicada como:

7.20.3 Funciones de gestión de memoria

1 El orden y la contigüidad del almacenamiento asignado por llamadas sucesivas a las funciones calloc, malloc y realloc no está especificado. El puntero que se devuelve si la asignación se realiza correctamente se alinea adecuadamente para que pueda asignarse a un puntero a cualquier tipo de objeto y luego usarse para acceder a dicho objeto o a una matriz de dichos objetos en el espacio asignado (hasta que el espacio se desasigne explícitamente) . La vida útil de un objeto asignado se extiende desde la asignación hasta la desasignación. [...]

Por lo tanto, no debe darse por sentado que el medio ambiente está haciendo el trabajo de liberación por usted. De lo contrario, se agregaría a la última oración: "O hasta que el programa finalice".

En otras palabras: no liberar memoria no es solo una mala práctica. Produce código no portátil y no conforme a C. Lo que al menos puede verse como "correcto, si lo siguiente: [...], es compatible con el entorno".

Pero en los casos en que no tiene ningún sistema operativo, nadie está haciendo el trabajo por usted (sé que generalmente no asigna y reasigna la memoria en los sistemas integrados, pero hay casos en los que es posible que desee).

Hablando en general, en forma de C simple (como se etiqueta el OP), esto simplemente está produciendo código erróneo y no portátil.

dhein
fuente
44
Un contraargumento es que si usted es un entorno integrado, usted, como desarrollador, sería mucho más exigente en la gestión de su memoria en primer lugar. Por lo general, esto es hasta el punto de preasignar memoria estática fija de antemano en lugar de tener mallocs / reallocs en tiempo de ejecución.
John Go-Soco
1
@lunarplasma: Si bien lo que está diciendo no es incorrecto, eso no cambia el hecho de lo que dice el estándar de idiomas, y todos los que actúan en contra de él, incluso por sentido común, están produciendo código limitado. Puedo entender si alguien dice "No tengo que preocuparme por eso", ya que hay suficientes casos en que está bien. PERO al menos uno debería saber POR QUÉ no tiene que preocuparse. y especialmente no lo omita siempre que una pregunta no esté relacionada con ese caso especial. Y dado que OP pregunta sobre C en general bajo aspectos teóricos (escolares). ¡No está bien decir "No es necesario"!
dhein
2
En la mayoría de los entornos donde no hay sistema operativo, no hay ningún medio a través del cual los programas puedan "terminar".
supercat
@supercat: Como he escrito antes: tienes razón al respecto. Pero si alguien pregunta al respecto con respecto a las razones de enseñanza y los aspectos escolares, no es correcto decir "No es necesario pensar en eso sicne la mayor parte del tiempo, no importa" La redacción y el comportamiento del idioma la definición se da por una razón, y solo porque la mayoría de los entornos lo manejan por usted, no puede decir que no hay necesidad de preocuparse. Ese es mi punto.
desde el
2
-1 para citar el estándar C mientras que la mayor parte NO se aplica en ausencia de un sistema operativo, ya que no hay tiempo de ejecución para proporcionar las características que los mandatos estándar, especialmente con respecto a la gestión de memoria y las funciones de biblioteca estándar (que obviamente también están ausentes) junto con el tiempo de ejecución / OS).
23

Por lo general, libero cada bloque asignado una vez que estoy seguro de que he terminado con él. Hoy, el punto de entrada de mi programa podría ser main(int argc, char *argv[]), pero mañana podría ser foo_entry_point(char **args, struct foo *f)y tecleado como un puntero de función.

Entonces, si eso sucede, ahora tengo una fuga.

Con respecto a su segunda pregunta, si mi programa tomara datos como a = 5, asignaría espacio para a, o reasignaría el mismo espacio en un a = "foo" posterior. Esto permanecería asignado hasta que:

  1. El usuario escribió 'desarmar a'
  2. Se ingresó a mi función de limpieza, ya sea dando servicio a una señal o el usuario escribió 'salir'

No puedo pensar en ningún sistema operativo moderno que no recupere memoria después de que un proceso finalice. Por otra parte, free () es barato, ¿por qué no limpiar? Como han dicho otros, las herramientas como valgrind son excelentes para detectar fugas de las que realmente debe preocuparse. Aunque los bloques que muestra se etiquetarían como 'todavía accesibles', es solo ruido adicional en la salida cuando intenta asegurarse de que no haya fugas.

Otro mito es " Si está en main (), no tengo que liberarlo ", esto es incorrecto. Considera lo siguiente:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

Si eso ocurrió antes de bifurcar / demonizar (y en teoría se ejecuta para siempre), su programa acaba de filtrar un tamaño indeterminado de t 255 veces.

Un buen programa bien escrito siempre debe limpiarse después de sí mismo. Libere toda la memoria, vacíe todos los archivos, cierre todos los descriptores, desvincule todos los archivos temporales, etc. Esta función de limpieza debe alcanzarse después de la terminación normal o al recibir varios tipos de señales fatales, a menos que desee dejar algunos archivos por ahí para que pueda detectar un bloqueo y reanudar

Realmente, sé amable con la pobre alma que tiene que mantener tus cosas cuando pasas a otras cosas ... entrégalas 'valgrind clean' :)

Tim Post
fuente
1
Y sí, una vez un compañero de equipo me dijo: "Nunca necesito llamar gratis () en main ()" <temblores>
Tim Post
free() is cheapa menos que tenga un billón de estructuras de datos con una relación compleja que tiene que liberar una por una, atravesar la estructura de datos para intentar liberar todo podría terminar aumentando significativamente su tiempo de cierre, especialmente si la mitad de esa estructura de datos ya está paginada al disco, sin ningún beneficio.
Mentira Ryan
3
@LieRyan Si tiene mil millones, como en literalmente mil millones de estructuras, definitivamente tiene otros problemas que necesitan un grado especializado de consideración, mucho más allá del alcance de esta respuesta particular :)
Tim Post
13

Está completamente bien dejar memoria sin liberar cuando salga; malloc () asigna la memoria del área de memoria llamada "el montón", y el montón completo de un proceso se libera cuando el proceso sale.

Dicho esto, una razón por la cual la gente todavía insiste en que es bueno liberar todo antes de salir es que los depuradores de memoria (por ejemplo, valgrind en Linux) detectan los bloques no alimentados como pérdidas de memoria, y si también tiene pérdidas de memoria "reales", se convierte en es más difícil detectarlos si también obtienes resultados "falsos" al final.

Antti Huima
fuente
1
¿Valgrind no hace un buen trabajo distinguiendo entre "filtrado" y "todavía accesible"?
Christoffer el
11
-1 para "completamente bien" Es una mala práctica de codificación dejar memoria asignada sin liberarla. Si ese código se extrajo en una biblioteca, entonces causaría memleaks por todo el lugar.
DevinB
55
+1 para compensar. Ver la respuesta de compie. freeen el exitmomento considerado perjudicial.
R .. GitHub DEJA DE AYUDAR A ICE
11

Si está utilizando la memoria que ha asignado, entonces no está haciendo nada malo. Se convierte en un problema cuando escribe funciones (que no sean main) que asignan memoria sin liberarla y sin ponerla a disposición del resto de su programa. Luego, su programa continúa ejecutándose con esa memoria asignada, pero no hay forma de usarlo. Su programa y otros programas en ejecución se ven privados de esa memoria.

Editar: no es 100% exacto decir que otros programas en ejecución están privados de esa memoria. El sistema operativo siempre puede permitirles usarlo a expensas de intercambiar su programa a la memoria virtual ( </handwaving>). Sin embargo, el punto es que si su programa libera memoria que no está utilizando, es menos probable que sea necesario un intercambio de memoria virtual.

Bill el lagarto
fuente
11

Este código generalmente funcionará bien, pero considere el problema de la reutilización del código.

Es posible que haya escrito un fragmento de código que no libera memoria asignada, se ejecuta de tal manera que la memoria se recupera automáticamente. Parece bien

Luego, alguien más copia su fragmento en su proyecto de tal manera que se ejecute mil veces por segundo. Esa persona ahora tiene una gran pérdida de memoria en su programa. No muy bueno en general, generalmente fatal para una aplicación de servidor.

La reutilización del código es típica en las empresas. Por lo general, la empresa posee todo el código que producen sus empleados y cada departamento puede reutilizar lo que sea que posea la empresa. Entonces, al escribir un código de "aspecto inocente", puede causar dolor de cabeza a otras personas. Esto puede hacerte despedir.

diente filoso
fuente
2
Puede valer la pena señalar la posibilidad no solo de que alguien copie el fragmento, sino también la posibilidad de que un programa que se escribió para realizar alguna acción en particular, una vez modificado, lo haga repetidamente. En tal caso, estaría bien que la memoria se asigne una vez y luego se use repetidamente sin haber sido liberada, pero asignar y abandonar la memoria para cada acción (sin liberarla) podría ser desastroso.
supercat
7

¿Cuál es el resultado real aquí?

Su programa filtró la memoria. Dependiendo de su sistema operativo, puede haberse recuperado.

La mayoría de los modernos de escritorio sistemas operativos hacen recuperar la memoria perdida en la terminación del proceso, por lo que es tristemente común a ignorar el problema, como puede verse por muchas otras respuestas aquí.)

Pero usted está confiando en una característica de seguridad no se debe confiar en ella, y su programa (o función) puede ser que funcione en un sistema donde este comportamiento hace resultado de una fuga "duro" de memoria, próxima vez.

Es posible que se esté ejecutando en modo kernel o en sistemas operativos antiguos / integrados que no emplean protección de memoria como compensación. (Las MMU ocupan el espacio del dado, la protección de la memoria cuesta ciclos de CPU adicionales y no es demasiado pedirle a un programador que se limpie después de sí mismo).

Puede usar y reutilizar la memoria de la manera que desee, pero asegúrese de desasignar todos los recursos antes de salir.

DevSolar
fuente
5

En realidad, hay una sección en el libro de texto en línea de OSTEP para un curso de pregrado en sistemas operativos que trata exactamente su pregunta.

La sección relevante es "Olvidando liberar memoria" en el capítulo API de memoria en la página 6, que da la siguiente explicación:

En algunos casos, puede parecer que no llamar a free () es razonable. Por ejemplo, su programa es de corta duración y pronto se cerrará; en este caso, cuando el proceso muere, el sistema operativo limpiará todas sus páginas asignadas y, por lo tanto, no se producirá ninguna pérdida de memoria per se. Si bien esto ciertamente "funciona" (ver el lado en la página 7), es probable que sea un mal hábito para desarrollar, así que tenga cuidado de elegir una estrategia de este tipo

Este extracto está en el contexto de la introducción del concepto de memoria virtual. Básicamente, en este punto del libro, los autores explican que uno de los objetivos de un sistema operativo es "virtualizar la memoria", es decir, dejar que cada programa crea que tiene acceso a un espacio de direcciones de memoria muy grande.

Detrás de escena, el sistema operativo traducirá "direcciones virtuales" que el usuario ve a direcciones reales que apuntan a la memoria física.

Sin embargo, compartir recursos como la memoria física requiere que el sistema operativo realice un seguimiento de los procesos que lo utilizan. Entonces, si un proceso finaliza, entonces está dentro de las capacidades y los objetivos de diseño del sistema operativo reclamar la memoria del proceso para que pueda redistribuir y compartir la memoria con otros procesos.


EDITAR: el lado mencionado en el extracto se copia a continuación.

A UN LADO: POR QUÉ NO SE ESCAPE NINGUNA MEMORIA UNA VEZ QUE SALE SU PROCESO

Cuando escribe un programa de corta duración, puede asignar algo de espacio usando malloc(). El programa se ejecuta y está a punto de completarse: ¿es necesario llamar free()varias veces antes de salir? Si bien parece incorrecto no hacerlo, no se perderá ningún recuerdo en ningún sentido real. La razón es simple: en realidad hay dos niveles de administración de memoria en el sistema. El sistema operativo realiza el primer nivel de gestión de la memoria, que entrega memoria a los procesos cuando se ejecutan, y la recupera cuando los procesos salen (o mueren). El segundo nivel de administración está dentro de cada proceso, por ejemplo dentro del montón cuando llama malloc()y free(). Incluso si no llamasfree()(y, por lo tanto, pierde memoria en el montón), el sistema operativo recuperará toda la memoria del proceso (incluidas esas páginas para código, pila y, según corresponda aquí, montón) cuando el programa termine de ejecutarse. No importa cuál sea el estado de su montón en su espacio de direcciones, el sistema operativo recupera todas esas páginas cuando el proceso muere, asegurando así que no se pierda memoria a pesar de que no la liberó.

Por lo tanto, para programas de corta duración, la pérdida de memoria a menudo no causa ningún problema operativo (aunque puede considerarse una forma deficiente). Cuando escribe un servidor de larga ejecución (como un servidor web o un sistema de administración de bases de datos, que nunca se cierra), la pérdida de memoria es un problema mucho mayor y eventualmente provocará un bloqueo cuando la aplicación se quede sin memoria. Y, por supuesto, la pérdida de memoria es un problema aún mayor dentro de un programa en particular: el sistema operativo en sí. Mostrándonos una vez más: aquellos que escriben el código del núcleo tienen el trabajo más difícil de todos ...

de la página 7 del capítulo API de memoria de

Sistemas operativos: Tres piezas fáciles
Remzi H. Arpaci-Dusseau y Andrea C. Arpaci-Dusseau Arpaci-Dusseau Books Marzo, 2015 (Versión 0.90)

Spenceryue
fuente
4

No hay peligro real en no liberar sus variables, pero si asigna un puntero a un bloque de memoria a un bloque de memoria diferente sin liberar el primer bloque, el primer bloque ya no es accesible, pero aún ocupa espacio. Esto es lo que se llama una pérdida de memoria, y si lo hace con regularidad, su proceso comenzará a consumir más y más memoria, eliminando los recursos del sistema de otros procesos.

Si el proceso es de corta duración, a menudo puede salirse con la suya ya que el sistema operativo recupera toda la memoria asignada cuando se completa el proceso, pero le aconsejaría que se acostumbrara a liberar toda la memoria para la que no tiene más uso.

Kyle Cronin
fuente
1
Quiero decir -1 para su primera declaración "no hay peligro", excepto que luego da una respuesta reflexiva sobre por qué existe peligro.
DevinB
2
A medida que avanzan los peligros, es bastante benigno: tomaré una pérdida de memoria por una falla predeterminada cualquier día.
Kyle Cronin
1
Muy cierto, y los dos preferiríamos ninguno = D
DevinB
2
@KyleCronin Me gustaría mucho y no tengo una violación de segmento de una pérdida de memoria, ya que ambos son errores graves y de segmentación son más fáciles de detectar. Con demasiada frecuencia, las pérdidas de memoria pasan desapercibidas o no se resuelven porque son "bastante benignas". Mi RAM y yo estamos totalmente en desacuerdo.
Dan Bechard el
@Dan Como desarrollador, seguro. Como usuario, tomaré la pérdida de memoria. Prefiero tener un software que funcione, aunque con pérdida de memoria, sobre un software que no funciona.
Kyle Cronin
3

Tienes toda la razón al respecto. En pequeños programas triviales donde una variable debe existir hasta la muerte del programa, no hay un beneficio real para desasignar la memoria.

De hecho, una vez estuve involucrado en un proyecto donde cada ejecución del programa era muy compleja pero relativamente de corta duración, y la decisión fue simplemente mantener la memoria asignada y no desestabilizar el proyecto cometiendo errores al desasignarlo.

Dicho esto, en la mayoría de los programas esto no es realmente una opción, o puede llevarlo a quedarse sin memoria.

Uri
fuente
2

Tiene razón, la memoria se libera automáticamente cuando el proceso finaliza. Algunas personas se esfuerzan por no hacer una limpieza exhaustiva cuando finaliza el proceso, ya que todo se entregará al sistema operativo. Sin embargo, mientras su programa se está ejecutando, debe liberar memoria no utilizada. Si no lo hace, eventualmente se acabará o causará una paginación excesiva si su conjunto de trabajo se vuelve demasiado grande.

Miguel
fuente
2

Si está desarrollando una aplicación desde cero, puede tomar algunas decisiones informadas sobre cuándo llamar gratis. Su programa de ejemplo está bien: asigna memoria, tal vez haga que funcione durante unos segundos y luego se cierre, liberando todos los recursos que reclama.

Sin embargo, si está escribiendo algo más: un servidor / aplicación de larga ejecución o una biblioteca para ser utilizada por otra persona, debe esperar llamar gratis a todo lo que malloc.

Ignorando el lado pragmático por un segundo, es mucho más seguro seguir el enfoque más estricto y obligarse a liberar todo lo que malloc. Si no tiene la costumbre de detectar pérdidas de memoria cada vez que codifica, podría generar fácilmente algunas pérdidas. En otras palabras, sí, puedes escapar sin eso; sin embargo, ten cuidado.

ojrac
fuente
0

Si un programa olvida liberar unos pocos megabytes antes de salir, el sistema operativo los liberará. Pero si su programa se ejecuta durante semanas a la vez y un bucle dentro del programa se olvida de liberar algunos bytes en cada iteración, tendrá una gran pérdida de memoria que consumirá toda la memoria disponible en su computadora a menos que la reinicie regularmente base => incluso las pequeñas pérdidas de memoria pueden ser malas si el programa se usa para una tarea realmente grande, incluso si originalmente no fue diseñado para una.

Gunter Königsmann
fuente
-2

Creo que sus dos ejemplos son en realidad solo uno: free()deberían ocurrir solo al final del proceso, lo cual, como usted señala, es inútil ya que el proceso está terminando.

Sin embargo, en su segundo ejemplo, la única diferencia es que permite un número indefinido de malloc(), lo que podría provocar la falta de memoria. La única forma de manejar la situación es verificar el código de retorno malloc()y actuar en consecuencia.

Mouviciel
fuente