¿Las pérdidas de memoria están bien? [cerrado]

231

¿Es aceptable tener una pérdida de memoria en su aplicación C o C ++?

¿Qué sucede si asigna algo de memoria y la usa hasta la última línea de código en su aplicación (por ejemplo, el destructor de un objeto global)? Mientras el consumo de memoria no aumente con el tiempo, ¿está bien confiar en el sistema operativo para liberar su memoria cuando su aplicación finalice (en Windows, Mac y Linux)? ¿Consideraría incluso esto una pérdida de memoria real si la memoria se usara continuamente hasta que el sistema operativo la liberara?

¿Qué pasa si una biblioteca de terceros te obliga a esta situación? ¿Se negaría a usar esa biblioteca de terceros sin importar cuán grandiosa podría ser?

Solo veo una desventaja práctica, y es que estas fugas benignas aparecerán con las herramientas de detección de fugas de memoria como falsos positivos.

Imbue
fuente
50
Si el consumo de memoria no crece con el tiempo, no es una fuga.
mpez0
44
La mayoría de las aplicaciones (incluidos todos los programas .NET) tienen al menos unos pocos buffers que se asignan una vez y nunca se liberan explícitamente, por lo que la definición de mpez0 es más útil.
Ben Voigt
2
Sí, si tienes memoria infinita.
usuario
Una fuga "benigna" (si existe) no es un falso positivo: es una fuga que se detectó muy correctamente. La detección de fugas, incluso para fugas que usted personalmente no desea reparar, es la razón principal de un detector de fugas para existir.
cHao
1
@ mpez0 "Si el consumo de memoria no crece con el tiempo, no es una fuga"? Esa no es la definición de una pérdida de memoria. Una pérdida es la memoria que se ha filtrado, lo que significa que no se ha liberado y ya no tiene referencia a ella, por lo tanto, es imposible que pueda volver a liberarla. Si crece o no es irrelevante.
Mecki

Respuestas:

329

No.

Como profesionales, la pregunta que no deberíamos hacernos es: "¿Alguna vez está bien hacer esto?" sino más bien "¿Alguna vez hay una buena razón para hacer esto?" Y "buscar esa pérdida de memoria es un dolor" no es una buena razón.

Me gusta mantener las cosas simples. Y la regla simple es que mi programa no debería tener pérdidas de memoria.

Eso también hace que mi vida sea simple. Si detecto una pérdida de memoria, la elimino, en lugar de ejecutar una estructura de árbol de decisión elaborada para determinar si se trata de una pérdida de memoria "aceptable".

Es similar a las advertencias del compilador: ¿la advertencia será fatal para mi aplicación en particular? Tal vez no.

Pero en última instancia, es una cuestión de disciplina profesional. Tolerar advertencias del compilador y tolerar pérdidas de memoria es un mal hábito que finalmente me morderá en la parte trasera.

Para llevar las cosas al extremo, ¿sería aceptable que un cirujano deje algún equipo operativo dentro de un paciente?

Aunque es posible que surja una circunstancia en la que el costo / riesgo de retirar ese equipo excede el costo / riesgo de dejarlo, y podría haber circunstancias en las que fuera inofensivo, si veía esta pregunta publicada en SurgeonOverflow.com y vi cualquier respuesta que no sea "no", socavaría seriamente mi confianza en la profesión médica.

-

Si una biblioteca de terceros me obligara a esta situación, me llevaría a sospechar seriamente la calidad general de la biblioteca en cuestión. Sería como si probara un automóvil y encontrara un par de arandelas y tuercas sueltas en uno de los portavasos; puede que no sea un gran problema en sí mismo, pero muestra una falta de compromiso con la calidad, por lo que consideraría alternativas.

JohnMcG
fuente
57
Verdadero y no verdadero al mismo tiempo. En definitiva, la mayoría de nosotros somos esclavos asalariados y cualquier deseo de artesanía debe quedar en segundo plano ante los requisitos del negocio. Si esa biblioteca tercera parte tiene una fuga y ahorra 2 semanas de trabajo, puede haber un caso de negocio para utilizarlo, etc ...
Cervo
3
Usaría la biblioteca de todos modos, si fuera algo que necesitara y no hubiera alternativas decentes, pero registraría un error con los encargados del mantenimiento.
tloach
77
Si bien personalmente elegiría exactamente la misma respuesta, hay programas que apenas liberan memoria. La razón es que están a) destinados a ejecutarse en sistemas operativos que liberan memoria, yb) diseñados para no ejecutarse por mucho tiempo. Restricciones raras para un programa de hecho, pero acepto esto como perfectamente válido.
2
Para agregar algunas razones para la verificación temprana: cuando sus herramientas de depuración se inundan con fugas "benignas", ¿cómo va a encontrar la "real"? Si agrega una función de lote, y de repente su fuga de 1K / hora se convierte en 1K / segundo.
peterchen
55
Hmm, ¿"no está perdiendo memoria" "perfecto"?
JohnMcG
80

No considero que sea una pérdida de memoria a menos que la cantidad de memoria "utilizada" siga creciendo. Tener algo de memoria inédita, aunque no es ideal, no es un gran problema a menos que la cantidad de memoria requerida siga creciendo.

Jim C
fuente
12
Técnicamente, una pérdida es la memoria que se asigna y se pierden todas las referencias a ella. No desasignar la memoria al final es simplemente vago.
Martin York
17
Si tiene una pérdida de memoria 1 vez de 4 GB, eso es un problema.
John Dibling
21
No importa si está creciendo o no. Otros programas no pueden usar la memoria si la tiene asignada.
Bill the Lizard, el
8
> Otros programas no pueden usar la memoria si la tiene asignada. Bueno, el sistema operativo siempre puede intercambiar su memoria en el disco y permitir que otras aplicaciones usen la RAM que no estaba aprovechando.
Max Lybbert
44
Si el programa dura muy poco, entonces una fuga podría no ser tan grave. Además, aunque NO es lo ideal, la paginación no es tan costosa como algunos lo hacen, porque el programa no está interesado en esa memoria (y, por lo tanto, no se intercambiará todo el tiempo), a menos que, por supuesto, tenga un GC ...
Arafangion
79

Vamos a obtener nuestras definiciones correctas, primero. Una pérdida de memoria es cuando la memoria se asigna dinámicamente, por ejemplo, con malloc(), y todas las referencias a la memoria se pierden sin el correspondiente libre. Una manera fácil de hacer uno es así:

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

Tenga en cuenta que cada vez alrededor del ciclo while (1), se asignan 1024 (+ gastos generales) bytes y la nueva dirección se asigna a vp; no queda puntero a los bloques mallocados anteriores. Se garantiza que este programa se ejecutará hasta que se agote el almacenamiento dinámico, y no hay forma de recuperar nada de la memoria mal asignada. La memoria está "goteando" del montón, para nunca volver a verse.

Sin embargo, lo que estás describiendo suena como

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

Usted asigna la memoria, trabaje con ella hasta que el programa finalice. Esto no es una pérdida de memoria; no daña el programa, y ​​toda la memoria se eliminará automáticamente cuando el programa finalice.

En general, debe evitar pérdidas de memoria. Primero, porque como la altitud sobre ti y el combustible en el hangar, la memoria que se ha filtrado y no se puede recuperar es inútil; segundo, es mucho más fácil codificar correctamente, sin pérdida de memoria, al principio que encontrar una pérdida de memoria más tarde.

Charlie Martin
fuente
Ahora considere unas pocas docenas de estas asignaciones. Ahora considere tener que mover el cuerpo "principal" a la rutina que se llama varias veces. Disfrutar. - Estoy de acuerdo con el sentimiento de que no es un problema tan grande en este escenario, pero los escenarios cambian. Como dicen, siempre escribe el código como si el tipo que lo mantiene sabe dónde vives.
peterchen
2
Bueno, el punto es que la memoria mal colocada y retenida hasta que el programa llama a _exit () no se "pierde".
Charlie Martin, el
1
Es una pérdida de memoria y puede afectar su programa. Las asignaciones futuras pueden fallar a partir de este proceso porque estoy seguro de que está comprobando que malloc devolvió no nulo en todas partes. mediante el uso excesivo de la memoria, como en una situación incrustada donde la memoria es escasa, esta podría ser la diferencia entre la vida y la muerte.
MikeJ
10
Mike, eso no es cierto. En un entorno C compatible, el final de main libera todos los recursos del proceso. En un entorno incrustado como el que usted describe, es posible que vea esa situación, pero no tendría un principal. Ahora, concederé que puede haber entornos incrustados defectuosos para los que esto no sería cierto, pero luego he visto entornos defectuosos que no podían hacer frente a + = correctamente también.
Charlie Martin
3
Sí, ahora has descubierto que si tienes mallocdemasiada memoria, es algo malo. Todavía no es una fuga . No es una fuga hasta ya menos que sea mallocd memoria a la que se pierde la referencia.
Charlie Martin
39

En teoría no, en la práctica depende .

Realmente depende de cuántos datos esté trabajando el programa, con qué frecuencia se ejecuta el programa y si se ejecuta constantemente o no.

Si tengo un programa rápido que lee una pequeña cantidad de datos, hace un cálculo y sale, nunca se notará una pequeña pérdida de memoria. Debido a que el programa no se ejecuta durante mucho tiempo y solo usa una pequeña cantidad de memoria, la pérdida será pequeña y se liberará cuando exista el programa.

Por otro lado, si tengo un programa que procesa millones de registros y se ejecuta durante mucho tiempo, una pequeña pérdida de memoria puede hacer que la máquina se caiga si se da el tiempo suficiente.

En cuanto a las bibliotecas de terceros que tienen fugas, si causan un problema, repare la biblioteca o encuentre una mejor alternativa. Si no causa un problema, ¿realmente importa?

vfilby
fuente
No sé si leíste mi pregunta completa o no. Estoy diciendo que la memoria se usa hasta el final de la aplicación. No crece con el tiempo. El único no no es que no hay una llamada para liberar / eliminar.
Imbue
2
Entonces no es realmente una pérdida de memoria. Una pérdida de memoria es pequeñas cantidades de memoria no utilizada pero no liberada, esta cantidad aumenta con el tiempo. De lo que estás hablando es de una gota de memoria. No se preocupe por esto a menos que su gota sea muy grande.
vfilby el
"Si no causa un problema, ¿realmente importa?" No, no importa en absoluto. Desearía que más personas tuvieran eso en lugar de hacerse religiosos.
Imbue
2
@John: Eso generalmente es menos una cuestión de desarrolladores flojos y más una cuestión de evolución del software. Todos cometemos errores, los errores son nuestro oficio; los hacemos, los arreglamos, eso es lo que hacemos. Siempre es un equilibrio entre el costo inicial y el mantenimiento a largo plazo, ese equilibrio nunca es sencillo.
vfilby
1
John, estoy 100% de acuerdo contigo. Imbum La pregunta es casi "¿cuánto aceptas?". Descuidado es descuidado. ¿Qué tal si dejo un camarón detrás de su monitor? hedor es hedor. Cada vez que nos derrumbamos, nuestra industria se hunde un poco. Si sabe que hay una fuga y sabe que la causó, entonces debe solucionarla.
baash05
37

Muchas personas parecen tener la impresión de que una vez que libera memoria, vuelve instantáneamente al sistema operativo y puede ser utilizada por otros programas.

Esto no es verdad Los sistemas operativos suelen gestionar la memoria en páginas de 4KiB. mallocy otros tipos de administración de memoria obtienen páginas del sistema operativo y las subadministran como mejor les parezca. Es muy probable que free()va a no volver a las páginas del sistema operativo, bajo el supuesto de que su programa malloc más memoria más adelante.

No digo que free()nunca devuelva memoria al sistema operativo. Puede suceder, especialmente si está liberando grandes extensiones de memoria. Pero no hay garantía.

El hecho importante: si no libera memoria que ya no necesita, se garantiza que más mallocs consumirán aún más memoria. Pero si libera primero, malloc podría reutilizar la memoria liberada.

¿Qué significa esto en la práctica? Significa que si sabe que su programa no requerirá más memoria de ahora en adelante (por ejemplo, está en la fase de limpieza), liberar memoria no es tan importante. Sin embargo, si el programa puede asignar más memoria más adelante, debe evitar pérdidas de memoria, especialmente las que pueden ocurrir repetidamente.

También vea este comentario para obtener más detalles sobre por qué liberar memoria justo antes de la finalización es malo.

Un comentarista no pareció entender que llamar free()no permite automáticamente que otros programas usen la memoria liberada. ¡Pero ese es el punto de esta respuesta!

Entonces, para convencer a la gente, demostraré un ejemplo donde free () hace muy poco bien. Para que las matemáticas sean fáciles de seguir, fingiré que el sistema operativo administra la memoria en páginas de 4000 bytes.

Suponga que asigna diez mil bloques de 100 bytes (por simplicidad, ignoraré la memoria adicional que se necesitaría para administrar estas asignaciones). Esto consume 1 MB, o 250 páginas. Si luego liberas 9000 de estos bloques al azar, te quedan solo 1000 bloques, pero están dispersos por todo el lugar. Estadísticamente, alrededor de 5 de las páginas estarán vacías. Los otros 245 tendrán cada uno al menos un bloque asignado en ellos. Eso equivale a 980 KB de memoria, que el sistema operativo no puede reclamar, ¡aunque ahora solo tiene 100 KB asignados!

Por otro lado, ahora puede malloc () 9000 bloques más sin aumentar la cantidad de memoria que su programa está atando.

Incluso cuando técnicamentefree() podría devolver la memoria al sistema operativo, es posible que no lo haga. necesita lograr un equilibrio entre operar rápidamente y ahorrar memoria. Y además, un programa que ya ha asignado mucha memoria y luego lo ha liberado es probable que lo vuelva a hacer. Un servidor web necesita manejar solicitud tras solicitud tras solicitud; tiene sentido mantener algo de memoria "floja" disponible para que no necesite pedirle memoria al sistema operativo todo el tiempo.free()

Artelius
fuente
1
¿Qué pasa si, otros programas requieren la memoria que su programa está reteniendo innecesariamente, por lo tanto, aunque no necesite más mallocs, libere () los espacios de memoria no utilizados :)
MN
2
Has perdido totalmente mi punto. Cuando liberas () memoria, ¡otros programas no pueden usarla! (A veces pueden, especialmente si liberas grandes bloques de memoria. ¡Pero a menudo, no pueden!) Editaré mi publicación para aclarar esto.
Artelius
27

Conceptualmente, no hay nada de malo en limpiar el sistema operativo después de ejecutar la aplicación.

Realmente depende de la aplicación y de cómo se ejecutará. Las fugas que ocurren continuamente en una aplicación que necesita ejecutarse durante semanas deben ser atendidas, pero una pequeña herramienta que calcule un resultado sin una necesidad de memoria demasiado alta no debería ser un problema.

Hay una razón por la cual muchos lenguajes de scripting no recolectan basura referencias cíclicas ... por sus patrones de uso, no es un problema real y por lo tanto sería un desperdicio de recursos tanto como la memoria desperdiciada.

kasperjj
fuente
Acerca de los lenguajes de scripting: Python usa el recuento pero tiene un GC solo para liberar referencias cíclicas En otros lenguajes, el programador a menudo evita referencias explícitamente cíclicas por completo, lo que crea otros problemas.
Blaisorblade
Las versiones anteriores de PHP no liberaban memoria, simplemente se ejecutaban de principio a fin creciendo en memoria: después de los 0.1 segundos de tiempo de ejecución, el script saldría y toda la memoria sería recuperada.
Arafangion
19

Creo que la respuesta es no, nunca permita una pérdida de memoria, y tengo algunas razones que no he visto explícitamente. Aquí hay excelentes respuestas técnicas, pero creo que la respuesta real depende de más razones sociales / humanas.

(Primero, tenga en cuenta que, como otros mencionaron, una fuga verdadera es cuando su programa, en cualquier momento, pierde el rastro de los recursos de memoria que ha asignado. En C, esto sucede cuando se malloc()dirige a un puntero y deja que ese puntero deje el alcance sin hacer un free()primero.)

El quid importante de su decisión aquí es el hábito. Cuando codifica en un idioma que usa punteros, va a usar mucho los punteros . Y los punteros son peligrosos; son la forma más fácil de agregar todo tipo de problemas graves a su código.

Cuando estás codificando, a veces vas a estar en la pelota y a veces estarás cansado, enojado o preocupado. Durante esos momentos algo distraídos, estás codificando más en piloto automático. El efecto del piloto automático no diferencia entre el código único y un módulo en un proyecto más grande. Durante esos momentos, los hábitos que establezca son los que terminarán en su base de código.

Así que no, nunca permita pérdidas de memoria por la misma razón por la que aún debe revisar sus puntos ciegos al cambiar de carril, incluso si es el único automóvil en la carretera en este momento. Durante los momentos en que su cerebro activo está distraído, los buenos hábitos son todo lo que puede salvarlo de pasos en falso desastrosos.

Más allá de la cuestión del "hábito", los indicadores son complejos y a menudo requieren una gran cantidad de poder cerebral para realizar un seguimiento mental. Es mejor no "enturbiar el agua" cuando se trata de utilizar punteros, especialmente cuando es nuevo en la programación.

También hay un aspecto más social. Mediante el uso adecuado de malloc()y free(), cualquiera que vea su código estará tranquilo; estás administrando tus recursos. Sin embargo, si no lo hace, sospecharán de inmediato un problema.

Tal vez hayas descubierto que la pérdida de memoria no hace daño a nada en este contexto, pero todos los encargados del mantenimiento de tu código también tendrán que pensarlo cuando lea ese código. Al usarlo free(), elimina la necesidad de considerar el problema

Finalmente, la programación es escribir un modelo mental de un proceso en un lenguaje inequívoco para que una persona y una computadora puedan entender perfectamente dicho proceso. Una parte vital de las buenas prácticas de programación es nunca introducir ambigüedades innecesarias.

La programación inteligente es flexible y genérica. La mala programación es ambigua.

Jason L
fuente
Me encanta la idea del hábito. Yo tambien estoy de acuerdo. Si veo una pérdida de memoria, siempre me pregunto qué otra esquina cortó el codificador. Especialmente si es obvio
baash05
Esta es la mejor respuesta con diferencia. He estado programando en C ++ durante 5 años y nunca he escrito una sola pérdida de memoria. La razón es que no escribo código que tiende a perder memoria. El buen diseño de C ++ rara vez se usa new, por lo que elimina la mayoría de las pérdidas de memoria de inmediato. Solo si absolutamente debes usarlo new. El resultado de eso newdebe colocarse inmediatamente en un puntero inteligente. Si sigue esas dos reglas, simplemente nunca perderá memoria (salvo un error en una biblioteca). El único caso restante son los shared_ptrciclos, en cuyo caso debe saber usar weak_ptr.
David Stone el
15

Creo que en su situación la respuesta puede ser que está bien. Pero definitivamente debe documentar que la pérdida de memoria es una decisión consciente. No desea que aparezca un programador de mantenimiento, coloque su código dentro de una función y lo llame un millón de veces. Entonces, si toma la decisión de que una fuga está bien, debe documentarla (EN GRANDES LETRAS) para quien tenga que trabajar en el programa en el futuro.

Si se trata de una biblioteca de terceros, puede estar atrapado. Pero definitivamente documente que se produce esta fuga.

Pero básicamente, si la pérdida de memoria es una cantidad conocida, como un búfer de 512 KB o algo así, no es un problema. Si la pérdida de memoria sigue creciendo como cada vez que llama a una llamada a la biblioteca, su memoria aumenta en 512 KB y no se libera, entonces puede tener un problema. Si lo documenta y controla la cantidad de veces que se ejecuta la llamada, puede ser manejable. Pero entonces realmente necesitas documentación porque si bien 512 no es mucho, 512 más de un millón de llamadas es mucho.

También debe verificar la documentación de su sistema operativo. Si se trataba de un dispositivo integrado, puede haber sistemas operativos que no liberen toda la memoria de un programa que se cierra. No estoy seguro, tal vez esto no sea cierto. Pero vale la pena estudiarlo.

Cervo
fuente
3
"Pero definitivamente debe documentar que la pérdida de memoria es una decisión consciente". Gracias al cielo. El mejor punto hecho hasta ahora.
pesófago
15

Voy a dar la respuesta impopular pero práctica de que siempre está mal liberar memoria a menos que hacerlo reduzca el uso de memoria de su programa . Por ejemplo, un programa que realiza una única asignación o una serie de asignaciones para cargar el conjunto de datos que utilizará durante toda su vida útil no necesita liberar nada. En el caso más común de un programa grande con requisitos de memoria muy dinámicos (piense en un navegador web), obviamente debería liberar memoria que ya no usa tan pronto como pueda (por ejemplo, cerrar una pestaña / documento / etc.) , pero no hay razón para liberar nada cuando el usuario selecciona clics "salir", y hacerlo es realmente perjudicial para la experiencia del usuario.

¿Por qué? Liberar memoria requiere tocar la memoria. Incluso si la implementación de malloc de su sistema no almacena metadatos adyacentes a los bloques de memoria asignados, es probable que camine estructuras recursivas solo para encontrar todos los punteros que necesita liberar.

Ahora, suponga que su programa ha trabajado con un gran volumen de datos, pero no ha tocado la mayor parte de él por un tiempo (nuevamente, el navegador web es un gran ejemplo). Si el usuario está ejecutando muchas aplicaciones, es probable que una buena parte de esos datos se haya intercambiado al disco. Si acaba de salir (0) o regresa de main, sale instantáneamente. Gran experiencia de usuario. Si se toma la molestia de intentar liberarlo todo, puede pasar 5 segundos o más intercambiando todos los datos nuevamente, solo para deshacerse de ellos inmediatamente después. Pérdida de tiempo del usuario. Pérdida de la vida útil de la batería del portátil. Residuos de desgaste en el disco duro.

Esto no es solo teórico. Cada vez que me encuentro con demasiadas aplicaciones cargadas y el disco comienza a agitarse, ni siquiera considero hacer clic en "salir". Llego a una terminal lo más rápido que puedo y escribo killall -9 ... porque sé que "salir" empeorará las cosas.

R .. GitHub DEJA DE AYUDAR AL HIELO
fuente
55
Me encanta esta cita de Raymond Chen: "El edificio está siendo demolido. No se moleste en barrer el piso y vaciar los botes de basura y borrar las pizarras blancas. Y no se alinee en la salida del edificio para que todos puedan mover su interior / "Imán a afuera. Todo lo que estás haciendo es hacer que el equipo de demolición espere a que termines estas inútiles tareas de limpieza". ( blogs.msdn.microsoft.com/oldnewthing/20120105-00/?p=8683 )
Andreas Magnusson
11

Estoy seguro de que alguien puede encontrar una razón para decir Sí, pero no seré yo. En lugar de decir que no, voy a decir que esta no debería ser una pregunta de sí / no. Hay formas de administrar o contener pérdidas de memoria, y muchos sistemas las tienen.

Hay sistemas de la NASA en dispositivos que abandonan la tierra que planean esto. Los sistemas se reiniciarán automáticamente de vez en cuando para que las pérdidas de memoria no sean fatales para la operación general. Solo un ejemplo de contención.

pearcewg
fuente
Eso es en realidad un ejemplo de envejecimiento del software. Fascinante tema de estudio.
Konrad Rudolph, el
Un reinicio automático de vez en cuando, ¿eh? NASA, ¿eh? (* mira los viejos CD de instalación de Microsoft Windows *) Esto explica mucho ...
Christian Severin
8

Si asigna memoria y la usa hasta la última línea de su programa, eso no es una fuga. Si asigna memoria y la olvida, incluso si la cantidad de memoria no está creciendo, eso es un problema. Esa memoria asignada pero no utilizada puede hacer que otros programas se ejecuten más lentamente o no se ejecuten.

Bill el lagarto
fuente
En realidad no, ya que si no se usa, simplemente se eliminará. Cuando la aplicación sale, se libera toda la memoria.
Eclipse el
Mientras esté asignado, otros programas no podrán usarlo. No se localizará si no lo desasigna.
Bill the Lizard, el
Por supuesto que sí, de eso se trata la memoria virtual. Puede tener 1 GB de RAM real y, sin embargo, tener 4 procesos, cada uno de los cuales asigna completamente 2 GB de memoria virtual (siempre que su archivo de página sea lo suficientemente grande).
Eclipse el
Por supuesto, obtendrá problemas desagradables de paginación si cada uno de esos procesos está utilizando activamente toda esa memoria.
Eclipse el
Está bien, entiendo de qué estás hablando ahora. Si desasigna la memoria que no está utilizando, reducirá la necesidad de paginación. Si lo mantiene asignado, su aplicación aún lo mantendrá cuando vuelva a paginar.
Bill the Lizard
8

Puedo contar con una mano la cantidad de fugas "benignas" que he visto con el tiempo.

Entonces la respuesta es un sí muy calificado.

Un ejemplo. Si tiene un recurso singleton que necesita un búfer para almacenar una cola circular o deque pero no sabe qué tan grande tendrá que ser el búfer y no puede permitirse la sobrecarga del bloqueo o de cada lector, entonces asigne un búfer que se duplique exponencialmente, pero no liberar los viejos perderá una cantidad limitada de memoria por cola / deque. El beneficio para estos es que aceleran cada acceso dramáticamente y pueden cambiar las asintóticas de las soluciones multiprocesador al nunca arriesgar la contención por un bloqueo.

He visto que este enfoque se usa con gran beneficio para cosas con recuentos muy claramente fijos, tales como deques de robo de trabajo por CPU, y en un grado mucho menor en el búfer utilizado para mantener el /proc/self/mapsestado singleton en el recolector de basura conservador de Hans Boehm para C / C ++, que se utiliza para detectar los conjuntos raíz, etc.

Si bien técnicamente es una fuga, ambos casos tienen un tamaño limitado y en el caso de robo de trabajo circular creíble hay una gran ganancia de rendimiento a cambio de un factor limitado de 2 aumentos en el uso de memoria para las colas.

Edward KMETT
fuente
1
Puede usar punteros de peligro para evitar la fuga.
Demi
8

Si asigna un montón de almacenamiento dinámico al comienzo de su programa y no lo libera al salir, eso no es una pérdida de memoria per se. Una pérdida de memoria es cuando su programa recorre una sección de código, y ese código asigna el montón y luego "pierde el rastro" sin liberarlo.

De hecho, no es necesario hacer llamadas a free () o eliminarlas justo antes de salir. Cuando el proceso se cierra, el sistema operativo recupera toda su memoria (este es ciertamente el caso con POSIX. En otros sistemas operativos, particularmente los integrados, YMMV).

La única precaución que tendré al no liberar la memoria al salir es que si alguna vez refactoriza su programa para que, por ejemplo, se convierta en un servicio que espera entrada, haga lo que sea que haga su programa, luego haga un bucle para esperar otra llamada de servicio, entonces lo que ha codificado puede convertirse en una pérdida de memoria.

nsayer
fuente
Siento disentir. Eso es "una pérdida de memoria per se".
Konrad Rudolph, el
No es una fuga hasta que "pierda" la referencia al objeto. Presumiblemente, si la memoria se usa durante la vida útil del programa, entonces no se pierde. Si la referencia no se pierde hasta la salida () se llama, entonces es absolutamente no una fuga.
nsayer
Amiga DOS fue la última revisión de O / SI que no se limpió detrás de los procesos. Sin embargo, tenga en cuenta que la memoria compartida de System V IPC se puede dejar incluso si ningún proceso la está utilizando.
Jonathan Leffler
Palm no libera memoria "filtrada" hasta que se sincroniza. llegó mucho después de la amiga. He ejecutado aplicaciones en mi emulador de palma que tenían fugas ... Nunca llegaron a mi palma real.
baash05
6

Esto es tan específico del dominio que apenas vale la pena responder. usa tu maldita cabeza.

  • sistema operativo del transbordador espacial: no, no se permiten pérdidas de memoria
  • Código de prueba de concepto de desarrollo rápido: arreglar todas esas pérdidas de memoria es una pérdida de tiempo.

y hay un espectro de situaciones intermedias.

El costo de oportunidad ($ $ $ $) de retrasar el lanzamiento de un producto para reparar todas las pérdidas de memoria, excepto las peores, generalmente es menor que cualquier sensación de ser "descuidado o poco profesional". Tu jefe te paga para que ganes dinero, no para tener sentimientos cálidos y confusos.

Dustin Getz
fuente
2
Actitud muy miope. Básicamente está diciendo que no hay necesidad de utilizar prácticas de programación fundamentalmente sólidas hasta que se descubra que un defecto es causado por esas prácticas. El problema es que el software que se escribe utilizando métodos descuidados tiende a tener más defectos que el software que no lo es.
John Dibling
1
No lo creo todo. Y la gestión de la memoria es más complicada que escribir métodos limpios.
Dustin Getz
1
Obviamente, Dustin trabaja en el mundo real como la mayoría de nosotros, donde trabajamos perpetuamente contra plazos insanos para mantenernos al día con la competencia. Por lo tanto, tratar con los errores debe hacerse de manera pragmática. Al perder demasiado tiempo en errores sin importancia en programas sin importancia, no podrá hacer sus cosas.
Wouter van Nifterick el
El problema con esta actitud es: ¿cuándo comienzas a arreglar las fugas? "Está bien, es una central eléctrica, pero es solo carbón, no uranio. ¿Por qué arreglar las fugas aquí?" - Aprendí en el mundo real que si no haces lo correcto desde el principio, todo el tiempo, simplemente nunca sucede. Esa actitud genera proyectos que están "99% completos" después de dos semanas y lo siguen siendo durante dos meses.
peterchen
5

Primero debe darse cuenta de que hay una gran diferencia entre una pérdida de memoria percibida y una pérdida de memoria real. Con mucha frecuencia, las herramientas de análisis informarán muchas pistas falsas y etiquetarán que algo se ha filtrado (memoria o recursos como identificadores, etc.) donde realmente no se ha filtrado. Muchas veces esto se debe a la arquitectura de la herramienta de análisis. Por ejemplo, ciertas herramientas de análisis informarán objetos de tiempo de ejecución como pérdidas de memoria porque nunca ve esos objetos liberados. Pero la desasignación se produce en el código de apagado del tiempo de ejecución, que la herramienta de análisis podría no ver.

Dicho esto, todavía habrá momentos en los que tendrá pérdidas de memoria reales que son muy difíciles de encontrar o muy difíciles de solucionar. Entonces, la pregunta es si alguna vez está bien dejarlos en el código.

La respuesta ideal es "no, nunca". Una respuesta más pragmática puede ser "no, casi nunca". Muy a menudo en la vida real tiene un número limitado de recursos y tiempo para resolver y una lista interminable de tareas. Cuando una de las tareas es eliminar las pérdidas de memoria, muy a menudo entra en juego la ley de rendimientos decrecientes. Podría eliminar, por ejemplo, el 98% de todas las pérdidas de memoria en una aplicación en una semana, pero el 2% restante podría llevar meses. En algunos casos, incluso podría ser imposible eliminar ciertas fugas debido a la arquitectura de la aplicación sin una refactorización importante del código. Debe sopesar los costos y beneficios de eliminar el 2% restante.

John Dibling
fuente
5

En este tipo de preguntas, el contexto lo es todo. Personalmente, no puedo soportar las fugas, y en mi código hago todo lo posible para solucionarlas si surgen, pero no siempre vale la pena reparar una fuga, y cuando la gente me paga por hora, ocasionalmente les dije que no valía la pena pagar por corregir una fuga en su código. Dejame darte un ejemplo:

Estaba probando un proyecto, haciendo un trabajo de perf y arreglando muchos errores. Hubo una fuga durante la inicialización de las aplicaciones que rastreé y entendí completamente. Arreglarlo correctamente habría requerido un día más o menos refactorizando un fragmento de código funcional. Podría haber hecho algo extravagante (como poner el valor en un global y aprovecharlo en algún momento, sé que ya no estaba en uso para liberar), pero eso habría causado más confusión al siguiente tipo que tuvo que tocar el código.

Personalmente, en primer lugar, no habría escrito el código de esa manera, pero la mayoría de nosotros no siempre trabajamos en bases de código impecables y bien diseñadas, y a veces hay que ver estas cosas de manera pragmática. La cantidad de tiempo que me habría tomado arreglar esa fuga de 150 bytes podría dedicarse a mejoras algorítmicas que redujeran los megabytes de ram.

En última instancia, decidí que no valía la pena reparar 150 fugas de 150 bytes para una aplicación que se usaba alrededor de un concierto de ram y se ejecutaba en una máquina dedicada, así que escribí un comentario diciendo que se había filtrado, lo que había que cambiar para arreglarlo y por qué no valió la pena en ese momento.

Louis Gerbarg
fuente
Inteligente. Especialmente porque la fuga fue durante la inicialización, lo que significa que no se acumularía durante el tiempo de ejecución de la aplicación.
Demi el
5

Si bien la mayoría de las respuestas se concentran en pérdidas de memoria reales (que nunca están bien, porque son un signo de codificación descuidada), esta parte de la pregunta me parece más interesante:

¿Qué sucede si asigna algo de memoria y la usa hasta la última línea de código en su aplicación (por ejemplo, el deconstructor de un objeto global)? Mientras el consumo de memoria no crezca con el tiempo, ¿está bien confiar en el sistema operativo para liberar su memoria cuando su aplicación finalice (en Windows, Mac y Linux)? ¿Consideraría incluso esto una pérdida de memoria real si la memoria se usara continuamente hasta que el sistema operativo la liberara?

Si se utiliza la memoria asociada, no puede liberarla antes de que finalice el programa. No importa si el programa gratuito se realiza mediante la salida del programa o el sistema operativo. Mientras esto esté documentado, para que el cambio no introduzca fugas de memoria reales, y mientras no haya un destructor de C ++ o una función de limpieza de C involucrados en la imagen. Un archivo no cerrado puede revelarse a través de un FILEobjeto filtrado , pero un fclose () faltante también puede hacer que el búfer no se vacíe.

Entonces, volviendo al caso original, en mi humilde opinión está perfectamente bien en sí mismo, tanto que Valgrind, uno de los detectores de fugas más potentes, tratará tales fugas solo si se solicita. En Valgrind, cuando sobrescribe un puntero sin liberarlo de antemano, se considera una pérdida de memoria, porque es más probable que vuelva a suceder y que el montón crezca sin cesar.

Entonces, no hay bloques de memoria nfreed que todavía sean accesibles. Uno podría asegurarse de liberarlos a todos en la salida, pero eso es solo una pérdida de tiempo en sí mismo. El punto es si podrían ser liberados antes . Reducir el consumo de memoria es útil en cualquier caso.

Blaisorblade
fuente
Wow ... alguien que sabe lo que es una pérdida de memoria.
Simon Buchan
4

Estoy de acuerdo con vfilby, depende. En Windows, tratamos las pérdidas de memoria como errores relativamente serios. Pero, depende mucho del componente.

Por ejemplo, las pérdidas de memoria no son muy graves para los componentes que se ejecutan raramente y por períodos limitados de tiempo. Estos componentes se ejecutan, hacen su trabajo y luego salen. Cuando salen, toda su memoria se libera implícitamente.

Sin embargo, las pérdidas de memoria en los servicios u otros componentes a largo plazo (como el shell) son muy graves. La razón es que estos errores 'roban' memoria con el tiempo. La única forma de recuperar esto es reiniciar los componentes. La mayoría de la gente no sabe cómo reiniciar un servicio o el shell, por lo que si el rendimiento de su sistema sufre, simplemente se reinicia.

Entonces, si tiene una fuga, evalúe su impacto de dos maneras

  1. Para su software y la experiencia de su usuario.
  2. Para el sistema (y el usuario) en términos de ser frugal con los recursos del sistema.
  3. Impacto de la solución en el mantenimiento y la fiabilidad.
  4. Probabilidad de causar una regresión en otro lugar.

Foredecker

Foredecker
fuente
3. Impacto en el mantenimiento del software.
Peterter
3

Incluso si está seguro de que su pérdida de memoria 'conocida' no causará estragos, no lo haga. En el mejor de los casos, allanará el camino para que pueda cometer un error similar y probablemente más crítico en un momento y lugar diferentes.

Para mí, preguntar esto es como preguntar "¿Puedo romper la luz roja a las 3 de la mañana cuando no hay nadie cerca?". ¡Claro que no puede causar ningún problema en ese momento, pero le proporcionará una palanca para hacer lo mismo en las horas pico!

Ather
fuente
3

No, no debe tener fugas que el sistema operativo limpiará por usted. La razón (no mencioné en las respuestas anteriores hasta donde pude comprobar) es que nunca se sabe cuándo su main () se reutilizará como una función / módulo en otro programa . Si su main () llega a ser una función frecuentemente llamada en el software de otras personas, este software tendrá una pérdida de memoria que la devora con el tiempo.

KIV


fuente
3

Supongo que está bien si está escribiendo un programa destinado a perder memoria (es decir, para probar el impacto de las pérdidas de memoria en el rendimiento del sistema).

Bip bip
fuente
3

Me sorprende ver tantas definiciones incorrectas de lo que realmente es una pérdida de memoria. Sin una definición concreta, una discusión sobre si es algo malo o no irá a ninguna parte.

Como algunos comentaristas han señalado correctamente, una pérdida de memoria solo ocurre cuando la memoria asignada por un proceso queda fuera del alcance en la medida en que el proceso ya no puede hacer referencia o eliminarla.

Un proceso que está tomando cada vez más memoria no necesariamente tiene fugas. Mientras sea capaz de hacer referencia y desasignar esa memoria, permanece bajo el control explícito del proceso y no se ha filtrado. El proceso puede estar mal diseñado, especialmente en el contexto de un sistema donde la memoria es limitada, pero esto no es lo mismo que una fuga. Por el contrario, perder el alcance de, por ejemplo, un búfer de 32 bytes sigue siendo una pérdida, a pesar de que la cantidad de memoria perdida es pequeña. Si cree que esto es insignificante, espere hasta que alguien ajuste un algoritmo alrededor de su llamada a la biblioteca y lo llame 10,000 veces.

No veo ninguna razón para permitir fugas en su propio código, por pequeño que sea. Los lenguajes de programación modernos como C y C ++ hacen todo lo posible para ayudar a los programadores a prevenir tales fugas y rara vez hay un buen argumento para no adoptar buenas técnicas de programación, especialmente cuando se combinan con instalaciones de lenguaje específicas, para evitar fugas.

Con respecto al código existente o de terceros, donde su control sobre la calidad o la capacidad de realizar un cambio puede ser muy limitado, dependiendo de la gravedad de la fuga, puede verse obligado a aceptar o tomar medidas atenuantes, como reiniciar el proceso regularmente para reducir El efecto de la fuga.

Es posible que no sea posible cambiar o reemplazar el código existente (con fugas) y, por lo tanto, puede estar obligado a aceptarlo. Sin embargo, esto no es lo mismo que declarar que está bien.

Componente 10
fuente
2

Realmente no es una fuga si es intencional y no es un problema a menos que sea una cantidad significativa de memoria, o podría convertirse en una cantidad significativa de memoria. Es bastante común no limpiar las asignaciones globales durante la vida útil de un programa. Si la fuga está en un servidor o aplicación de larga ejecución, crece con el tiempo, entonces es un problema.

Sanjaya R
fuente
2

Creo que has respondido tu propia pregunta. El mayor inconveniente es cómo interfieren con las herramientas de detección de pérdida de memoria, pero creo que ese inconveniente es un ENORME inconveniente para ciertos tipos de aplicaciones.

Trabajo con aplicaciones de servidor heredadas que se supone que son sólidas, pero que tienen fugas y los globales se interponen en el camino de las herramientas de detección de memoria. Tiene mucha importancia.

En el libro "Collapse" de Jared Diamond, el autor se pregunta qué estaba pensando el hombre que cortó el último árbol en la Isla de Pascua, el árbol que habría necesitado para construir una canoa para salir de la isla. Me pregunto sobre el día hace muchos años cuando ese primer global se agregó a nuestra base de código. Ese fue el día que debería haber sido atrapado.

Corey Trager
fuente
2

Veo el mismo problema que todas las preguntas de escenario como esta: ¿Qué sucede cuando el programa cambia, y de repente esa pequeña pérdida de memoria se llama diez millones de veces y el final de su programa está en un lugar diferente, por lo que importa? Si está en una biblioteca, entonces registre un error con los mantenedores de la biblioteca, no ponga una fuga en su propio código.

tloach
fuente
En ese caso, el impacto de la pérdida de memoria cambia, y debe reevaluar la prioridad de tapar la pérdida.
John Dibling
@ John: Mejor al menos documentar la fuga entonces. Incluso entonces, no confiaría en alguien para no ignorar un gran comentario rojo intermitente y copiar y pegar el código de fuga de todos modos. Prefiero no darle a alguien la capacidad de hacerlo en primer lugar.
tloach
2

Contestaré no.

En teoría, el sistema operativo se limpiará después de ti si dejas un desastre (ahora eso es grosero, pero dado que las computadoras no tienen sentimientos, podría ser aceptable). Pero no puede anticipar todas las situaciones posibles que pueden ocurrir cuando se ejecuta su programa. Por lo tanto (a menos que pueda realizar una prueba formal de algún comportamiento), crear pérdidas de memoria es simplemente irresponsable y descuidado desde un punto de vista profesional.

Si un componente de terceros pierde memoria, este es un argumento muy fuerte en contra de usarlo, no solo por el efecto inminente sino también porque muestra que los programadores trabajan de manera descuidada y que esto también podría afectar otras métricas. Ahora, al considerar sistemas heredados, esto es difícil (considere los componentes de navegación web: que yo sepa, todos pierden memoria), pero debería ser la norma.

Konrad Rudolph
fuente
2

Históricamente, sí importó en algunos sistemas operativos en algunos casos extremos. Estos casos extremos podrían existir en el futuro.

Aquí hay un ejemplo, en SunOS en la era de Sun 3, había un problema si un proceso usaba exec (o más tradicionalmente fork y luego exec), el nuevo proceso posterior heredaría la misma huella de memoria que el padre y no podría reducirse . Si un proceso padre asignó 1/2 concierto de memoria y no lo liberó antes de llamar a exec, el proceso hijo comenzaría a usar ese mismo 1/2 concierto (a pesar de que no estaba asignado). Este comportamiento fue mejor exhibido por SunTools (su sistema de ventanas predeterminado), que era un cerdo de memoria. Cada aplicación que generó se creó a través de fork / exec y heredó la huella de SunTools, llenando rápidamente el espacio de intercambio.

pedestal
fuente
2

Esto ya se discutió hasta la saciedad . La conclusión es que una pérdida de memoria es un error y debe repararse. Si una biblioteca de terceros pierde memoria, uno se pregunta qué más le pasa, ¿no? Si estuvieras construyendo un automóvil, ¿utilizarías un motor que ocasionalmente pierde aceite? Después de todo, alguien más hizo el motor, así que no es tu culpa y no puedes arreglarlo, ¿verdad?

Dima
fuente
Pero si tenía un automóvil con un motor que ocasionalmente gotea aceite, ¿gasta dinero para repararlo o vigila los niveles de aceite y lo repone de vez en cuando? La respuesta depende de todo tipo de factores.
delgado
No se trata de tener un auto. Esto se trata de construir un auto. Si obtiene una biblioteca de terceros con pérdidas de memoria y absolutamente tiene que usarla, entonces vive con ella. Pero si usted es el que escribe un sistema o una biblioteca, es su responsabilidad asegurarse de que esté libre de errores.
Dima
+1 trátalo como cualquier otro error. (Eso no significa "arreglar al instante" en mi libro, pero "necesita ser arreglado" con seguridad)
Peterter
2

En general, una pérdida de memoria en una aplicación independiente no es fatal, ya que se limpia cuando sale del programa.

¿Qué haces para los programas de servidor que están diseñados para que no salgan?

Si usted es el tipo de programador que no diseña e implementa código donde los recursos se asignan y liberan correctamente, entonces no quiero tener nada que ver con usted o su código. Si no le importa limpiar su memoria perdida, ¿qué pasa con sus cerraduras? ¿Los dejas colgando allí también? ¿Dejas pequeños montones de archivos temporales en varios directorios?

¿Fuga esa memoria y deja que el programa la limpie? No absolutamente no. Es un mal hábito, que conduce a errores, errores y más errores.

Limpia después de ti mismo. Yo mamá no trabajas más aquí.

EvilTeach
fuente
He trabajado en programas de servidor que usan procesos deliberadamente en lugar de subprocesos, por lo que las pérdidas de memoria y las fallas de segmentación causan daños limitados.
delgado
Enfoque interesante Me preocuparía un poco los procesos que no salen y continúan engullendo memoria.
EvilTeach
2

Como regla general, si tiene pérdidas de memoria que cree que no puede evitar, debe pensar más en la propiedad de los objetos.

Pero a su pregunta, mi respuesta en pocas palabras es en el código de producción, sí. Durante el desarrollo, no . Esto puede parecer al revés, pero aquí está mi razonamiento:

En la situación que describe, donde la memoria se mantiene hasta el final del programa, está perfectamente bien no liberarla. Una vez que finalice su proceso, el sistema operativo se limpiará de todos modos. De hecho, podría mejorar la experiencia del usuario: en un juego en el que he trabajado, los programadores pensaron que sería más limpio liberar toda la memoria antes de salir, ¡lo que provocaría que el apagado del programa dure hasta medio minuto! Un cambio rápido que simplemente llamó a exit () en su lugar hizo que el proceso desapareciera de inmediato, y devolvió al usuario al escritorio donde quería estar.

Sin embargo, tiene razón acerca de las herramientas de depuración: arrojarán un ataque, y todos los falsos positivos pueden hacer que encontrar su verdadera memoria pierda un dolor. Y debido a eso, siempre escriba código de depuración que libere la memoria y desactívelo cuando realice el envío.

Enno
fuente