2010-05-04 11 views
16

que estaba leyendo la entrada C# en Wikipedia, y encontré:¿Por qué no se permite el uso de free() en los idiomas recogidos de basura?

memoria administrada no puede ser liberado de forma explícita; en cambio, se recoge basura automáticamente.

Por qué es que en las lenguas con gestión automática de memoria, gestión manual es ni siquiera permitió ? Puedo ver que en la mayoría de los casos no sería necesario, pero ¿no sería útil cuando tienes poco memoria y no quieres confiar en que el GC sea inteligente?

Respuesta

4

La recolección de basura impone la seguridad del tipo de un asignador de memoria al garantizar que las asignaciones de memoria nunca se alias. Es decir, si una parte de la memoria se ve actualmente como un tipo T, el asignador de memoria puede garantizar (con la recolección de elementos no utilizados) que, si bien esa referencia está activa, siempre se referirá a T. Más específicamente, significa que el asignador de memoria nunca devolverá esa memoria como un tipo diferente.

Ahora, si un asignador de memoria permite el manual free() y utiliza la recolección de elementos no utilizados, debe asegurarse de que la memoria free() 'd no esté referenciada por nadie más; en otras palabras, que la referencia que pasa al free() es la única referencia a esa memoria. La mayoría de las veces, esto es prohibitivamente caro debido a una llamada arbitraria al free(), por lo que la mayoría de los asignadores de memoria que usan recolección de basura no lo permiten.

Eso no quiere decir que no sea posible; si pudieras expresar un tipo de referencia única, podrías administrarlo manualmente. Pero en ese punto sería más fácil dejar de usar un lenguaje de GC o simplemente no preocuparse por ello.

+0

Esta es una buena descripción de la seguridad de la memoria que menciono en mi respuesta. Gracias. – Novelocrat

+0

Acepté esta respuesta, ya que proporciona un caso sólido en el que la función manual free() podría interferir con el _trabajo_ de un GC. Las otras respuestas, hasta donde yo entiendo, dan solo las formas en que free() va en contra del _intent_ de tener un GC. – sundar

+0

@sundar, gracias. En realidad, nunca consideré la seguridad del tipo de cumplimiento de GC hasta que leí algo al respecto en una presentación sobre D. – MSN

0

No puedo decir que es la respuesta, pero uno que viene a la mente es que si se puede free, puede accidentalmente doble free un puntero/referencia o, peor aún, utilice uno después de liberación. Lo cual frustra el principal punto de uso de lenguajes como C#/java/etc.

Por supuesto, una posible solución sería tener su free tomar su argumento por referencia y configurarlo en null después de la liberación. Pero entonces, ¿qué pasa si pasan un r-value así: free(whatever()). Supongo que podría tener una sobrecarga para las versiones r-value, pero ni siquiera sé si C# admite tal cosa :-P.

Al final, incluso eso sería insuficiente porque, como se ha señalado, puede tener múltiples referencias al mismo objeto. Establecer uno en null no haría nada para evitar que los demás accedan al objeto desasignado ahora.

+1

Anular el puntero pasado no es la solución a nada. Los punteros se pueden duplicar y, por lo tanto, desreferenciados por otro código. Ver mi respuesta – Novelocrat

+0

por supuesto, no intenté dar a entender que era una solución efectiva. –

0

Si se encuentra en una situación en la que "no quiere confiar en que el GC sea inteligente", entonces lo más probable es que haya elegido el marco para su tarea incorrectamente. En .net puede manipular GC un poco (http://msdn.microsoft.com/library/system.gc.aspx), en Java no estoy seguro.

Creo que no puedes llamar gratis porque comienzas a hacer una tarea de GC. La eficiencia de GC puede de alguna manera estar garantizada en general cuando hace las cosas de la manera que le parece mejor y las hace cuando lo decide. Si los desarrolladores interfieren con GC, puede disminuir su eficiencia general.

+0

Se trata principalmente de seguridad, no de eficiencia. – Novelocrat

+0

Aunque GC a menudo puede mejorar el rendimiento, muy pocos sistemas usan colectores con propiedades comprobables, ya que pagan un alto costo en gastos generales para mantener esas garantías. – Novelocrat

+0

@Novelocrat No estoy de acuerdo con la seguridad. Creo que se trata de la integridad de GC. "Dale a Caesar lo que es de Caesar", deja que GC haga su trabajo. .net/java todavía le da la capacidad de disparar a su pie. – Andrey

-1

Se permite la gestión manual. Por ejemplo, en Ruby llamando al GC.start, liberará todo lo que se puede liberar, aunque no puede liberar cosas individualmente.

+1

Eso no es realmente lo mismo. –

0

Curiosamente, usted tiene acceso al recolector de basura a través de System.GC - Aunque por todo lo que he leído, es muy recomendable que permita que el GC se administre solo.

me aconsejaron vez de usar las siguientes 2 líneas por un proveedor de 3 ª parte para hacer frente a un problema de recolección de basura con un objeto DLL o COM o algún -tales:

// Force garbage collection (cleanup event objects from previous run.) 
GC.Collect();   // Force an immediate garbage collection of all generations 
GC.GetTotalMemory(true); 

Dicho esto, yo wouldn' Me molesto con System.GC a menos que supiera exactamente lo que sucedía debajo del capó. En este caso, el consejo del vendedor de terceros "solucionó" el problema con el que estaba tratando con respecto a su código. Pero no puedo evitar preguntarme si esto fue en realidad una solución para su código roto ...

+2

Probablemente fue. El único caso en el que alguna vez considero llamar a GC.Collect es si tenía un objeto enorme y de larga vida que acababa de descartar. El GC asume que si un objeto ha existido durante mucho tiempo, lo mantendrás un tiempo más largo, por lo que todo lo que haya sobrevivido varias veces no se recopilará a menos que se realice una recolección completa. Dicho esto, es un caso raro que requiere que lo hagas manualmente. – cHao

+0

Eso no es gestión de memoria manual. – Novelocrat

+0

@Novelocrat: nunca dije que fuera. Todo lo que dije fue "Interesante ..." – Pretzel

1

Llamar a GC.Collect casi siempre es mejor que tener un método explícito free. Llamar a free tendría sentido solo para punteros/refs de objetos a los que se hace referencia de la nada.Eso es algo que es propenso a errores, ya que existe la posibilidad de que su llamada free para el tipo de puntero equivocado.

Cuando el entorno de ejecución hace referencia contando monitoreo para usted, sepa cuál punteros se pueden liberar de forma segura, y cuáles no, por lo que dejar que el GC decidir qué memoria se puede liberar evita una clase agujero de bichos feos. Uno podría pensar en una implementación en tiempo de ejecución con GC y free donde llamar explícitamente a free para un único bloque de memoria podría ser mucho más rápido que ejecutar un completo GC.Collect (pero no espere liberar cada bloque de memoria "a mano" para que sea más rápido que el GC). Pero creo que los diseñadores de C#, CLI (y otros lenguajes con recolectores de basura como Java) han decidido favorecer la robustez y la seguridad sobre la velocidad aquí.

+1

Los entornos de GC en realidad pueden ser de mayor rendimiento que el entorno manual equivalente, porque la asignación a menudo es simplemente un puntero. Los colectores en movimiento también pueden mejorar la localidad de referencia a medida que se ejecuta el programa. – Novelocrat

+0

Además, GC en la JVM y la CLR no se basan en el recuento de referencias, porque eso no tiene en cuenta las estructuras cíclicas. Trazan desde un conjunto raíz y marcan o mueven objetos que se alcanzan, descartando objetos que nunca se alcanzan. – Novelocrat

+0

@Novelocrat: tienes razón en lo que dices sobre JVM y CLI, debería haber dicho sobre "monitoreo de referencia", no "conteo de referencia". Sin embargo, escribí intencionalmente "llamar gratis por un único bloque de memoria", no "llamar gratis para duplicar el comportamiento del GC de orificio". –

10

Los idiomas con gestión de memoria automática están diseñados para proporcionar garantías sustanciales de seguridad de la memoria que no pueden ofrecerse en presencia de una gestión de memoria manual.

Entre los problemas impedido son

  • doble free() s
  • Calling free() en un puntero a la memoria que no es propietario, lo que lleva al acceso ilegal en otros lugares
  • Calling free() en un puntero que no era el valor de retorno de una función de asignación, como tomar la dirección de algún objeto en la pila o en el medio de una matriz u otra asignación.
  • Desreferenciar un puntero a la memoria que ya ha sido free() d

Además, la gestión automática puede resultar en un mejor rendimiento cuando los movimientos GC viven objetos a una zona consolidada. Esto mejora la localidad de referencia y, por lo tanto, el rendimiento de la memoria caché.

+1

en llamadas .net Eliminar de forma incorrecta puede crear los mismos problemas. – Andrey

+3

Solo si la implementación Dispose está mal codificada – thecoop

+0

@Andrey: En .NET, si se llama a 'Dispose' en' Wizbang' se puede convertir en un objeto 'Wizbang' inútil que ya no puede realizar acciones específicas de' Wizbang', pero seguirá siendo un 'Objeto' válido, y métodos como' GetType', 'Equals', etc. deberían seguir funcionando. Mientras que cualquier referencia permanezca en el 'Wizbang', todas las referencias a ella serán referencias a un' Wizbang' muerto. Por el contrario, en un sistema de asignación manual, la llamada 'libre' en un puntero al que existen otras copias puede hacer que esas otras copias se conviertan en referencias a algo completamente diferente. – supercat

0

Muchas de las otras respuestas proporcionan buenas explicaciones sobre cómo funciona el GC y cómo debe pensar al programar en un sistema de tiempo de ejecución que proporciona un GC.

Me gustaría añadir un truco que trato de tener en cuenta cuando programo en lenguajes de GC. La regla es esta: "es importante dejar los punteros lo antes posible". Al eliminar los punteros quiero decir que ya no apunto a los objetos que ya no usaré.Por ejemplo, esto se puede hacer en algunos idiomas estableciendo una variable en Nulo. Esto puede verse como una pista para el recolector de basura de que está bien recoger este objeto, siempre que no haya otros indicadores para él.

+2

No rechazaré, pero en algunos sistemas (por ejemplo, .NET), el JIT/GC se coludió para hacer un seguimiento de las referencias en vivo cuerpos de método. Esto significa que tan pronto como la variable ya no se acceda, lo que sea que apunte sea elegible para la recolección (suponiendo que no haya otras referencias pendientes). Establecer la variable en nulo en realidad * prolonga * la vida útil de la variable. –

+0

@Damien: un compilador o tiempo de ejecución que ejecutó su análisis en una representación de Asignación única estática del programa no tendría un problema con la asignación de valores nulos.De hecho, si esa asignación domina la asignación original, entonces ofrece un límite duro a la vida de este último. – Novelocrat

1

En los sistemas que permiten que los objetos se liberen manualmente, las rutinas de asignación deben buscar en una lista de áreas de memoria liberadas para encontrar algo de memoria libre. En un sistema basado en recolección de basura, cualquier memoria libre inmediatamente disponible estará al final del montón. En general, es más rápido y más fácil para el sistema ignorar áreas de memoria no utilizadas en el medio del montón de lo que sería tratar de asignarlas.

0

¿Por qué querrías usar free()? Supongamos que tiene una gran cantidad de memoria que desea desasignar.

Una forma de hacerlo es llamar al recolector de basura o dejar que se ejecute cuando el sistema lo desee. En ese caso, si no se puede acceder al gran trozo de memoria, será desasignado. (Los recolectores de basura modernos son bastante inteligentes.) Eso significa que, si no se desasignó, aún se puede acceder.

Por lo tanto, si puede deshacerse de él con free() pero no es el recolector de basura, algo puede acceder a ese fragmento (y no a través de un puntero débil si el lenguaje tiene el concepto), lo que significa que se queda con el equivalente del idioma de un puntero colgante.

El lenguaje puede defenderse contra los doble-libres o intentar liberar memoria no asignada, pero la única forma en que puede evitar punteros colgantes es aboliendo free(), o modificando su significado para que ya no tenga uso.

0

¿Por qué en idiomas con administración automática de memoria, la administración manual no está permitida? Puedo ver que en la mayoría de los casos no sería necesario, pero ¿no sería útil cuando tienes poco memoria y no quieres confiar en que el GC sea inteligente?

En la gran mayoría de las lenguas de basura recogida y máquinas virtuales que no tiene sentido para ofrecer una función free aunque casi siempre se puede utilizar la FFI para asignar y memoria no administrada libre fuera de la máquina virtual gestionado si así lo desea.

Hay dos razones principales por las free está ausente de idiomas recogida de basura: la seguridad

  1. memoria.
  2. Sin punteros.

En cuanto a la seguridad de la memoria, una de las principales motivaciones detrás de la administración automática de la memoria es eliminar la clase de errores causados ​​por la administración incorrecta de la memoria manual. Por ejemplo, con la gestión manual de la memoria llamar al free con el mismo puntero dos veces o con un puntero incorrecto puede dañar las propias estructuras de datos del administrador de memoria y causar bloqueos no determinísticos en el programa (cuando el administrador de memoria alcance sus datos dañados). Esto no puede suceder con la gestión automática de la memoria, pero al exponer free se abriría nuevamente esta lata de gusanos.

En cuanto a los punteros, la función free libera un bloque de memoria asignada en una ubicación especificada por un puntero al administrador de memoria. Los lenguajes y máquinas virtuales recolectados de basura reemplazan los punteros con un concepto más abstracto llamado referencias.La mayoría de los GC de producción se mueven, lo que significa que el código de alto nivel hace referencia a un valor u objeto, pero la ubicación subyacente en la memoria puede cambiar ya que la VM es capaz de mover bloques de memoria asignados sin el conocimiento del lenguaje de alto nivel. Esto se usa para compactar el montón, prevenir la fragmentación y mejorar la localidad.

Así que hay buenas razones para no tener free cuando tiene un GC.

Cuestiones relacionadas