Sé por leer la documentación de Microsoft que el uso "primario" de la IDisposable
interfaz es limpiar recursos no administrados.
Para mí, "no administrado" significa cosas como conexiones de bases de datos, sockets, manejadores de ventanas, etc. Pero, he visto código donde el Dispose()
método se implementa para liberar recursos administrados , lo que me parece redundante, ya que el recolector de basura debería ocuparse de eso para ti.
Por ejemplo:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Mi pregunta es, ¿esto hace que el recolector de basura libere la memoria utilizada MyCollection
más rápido de lo normal?
editar : Hasta ahora, la gente ha publicado algunos buenos ejemplos de uso de IDisposable para limpiar recursos no administrados, como conexiones de bases de datos y mapas de bits. Pero suponga que _theList
en el código anterior contiene un millón de cadenas, y desea liberar esa memoria ahora , en lugar de esperar al recolector de basura. ¿Lo lograría el código anterior?
IDisposable
No marca nada. ElDispose
método hace lo que tiene que hacer para limpiar los recursos utilizados por la instancia. Esto no tiene nada que ver con GC.IDisposable
. Y es por eso que dije que la respuesta aceptada no responde la pregunta prevista del OP (y la edición de seguimiento) sobre si IDisposable ayudará a <i> liberar memoria </i>. ComoIDisposable
no tiene nada que ver con liberar memoria, solo recursos, entonces, como dijiste, no es necesario configurar las referencias administradas como nulas, que es lo que OP estaba haciendo en su ejemplo. Entonces, la respuesta correcta a su pregunta es "No, no ayuda a liberar memoria más rápido. De hecho, no ayuda a liberar memoria en absoluto, solo recursos". Pero de todos modos, gracias por tu aporte.Respuestas:
El punto de disposición es liberar recursos no administrados. Debe hacerse en algún momento, de lo contrario nunca se limpiarán. El recolector de basura no sabe cómo llamar
DeleteHandle()
a una variable de tipoIntPtr
, no sabe si necesita o no llamarDeleteHandle()
.El objeto que ha creado necesita exponer algún método, que el mundo exterior pueda llamar, para limpiar recursos no administrados. El método se puede nombrar como quieras:
o
Pero en cambio hay un nombre estandarizado para este método:
Incluso se creó una interfaz
IDisposable
que tiene ese único método:Entonces, hace que su objeto exponga la
IDisposable
interfaz, y de esa manera promete que ha escrito ese método único para limpiar sus recursos no administrados:Y tu estas listo. Excepto que puedes hacerlo mejor.
¿Qué sucede si su objeto ha asignado un System.Drawing.Bitmap de 250 MB (es decir, la clase de mapa de bits administrado .NET) como algún tipo de búfer de cuadros? Claro, este es un objeto .NET administrado, y el recolector de basura lo liberará. Pero, ¿de verdad quieres dejar 250 MB de memoria simplemente sentado allí, esperando que el recolector de basura finalmente llegue y lo libere? ¿Qué pasa si hay una conexión de base de datos abierta ? Seguramente no queremos que esa conexión permanezca abierta, esperando que el GC finalice el objeto.
Si el usuario ha llamado
Dispose()
(lo que significa que ya no planea usar el objeto), ¿por qué no deshacerse de esos mapas de bits y conexiones de bases de datos derrochadores?Entonces ahora nosotros:
Así que vamos a actualizar nuestro
Dispose()
método para deshacernos de esos objetos administrados:¡Y todo está bien, excepto que puedes hacerlo mejor !
¿Qué pasa si la persona olvidó llamar
Dispose()
a su objeto? ¡Entonces perderían algunos recursos no administrados !Si la persona olvidó llamar
Dispose()
, ¡ aún podemos salvar su tocino! Todavía tenemos una forma de llamarlo para ellos: cuando el recolector de basura finalmente consigue en torno a la liberación (es decir finalización) nuestro objeto.La destrucción de nuestro objeto por parte del recolector de basura es el momento perfecto para liberar esos molestos recursos no administrados. Hacemos esto anulando el
Finalize()
método.Pero hay un error en ese código. Verá, el recolector de basura se ejecuta en un subproceso de fondo ; no sabes el orden en que se destruyen dos objetos. Es completamente posible que en su
Dispose()
código, el objeto administrado del que está tratando de deshacerse (porque quería ser útil) ya no esté allí:Entonces, lo que necesita es una forma de
Finalize()
saberDispose()
que no debe tocar ningún recurso administrado (porque es posible que ya no esté allí ), sin dejar de liberar recursos no administrados.El patrón estándar para hacer esto es tener
Finalize()
yDispose()
ambos llamar a un tercer método (!); donde pasa un refrán booleano si lo está llamandoDispose()
(en lugar deFinalize()
), lo que significa que es seguro liberar recursos administrados.Este método interno podría recibir un nombre arbitrario como "CoreDispose" o "MyInternalDispose", pero es tradición llamarlo
Dispose(Boolean)
:Pero un nombre de parámetro más útil podría ser:
Y cambia su implementación del
IDisposable.Dispose()
método a:y tu finalizador para:
¡Y todo está bien, excepto que puedes hacerlo mejor !
Si el usuario llama
Dispose()
a su objeto, entonces todo se ha limpiado. Más tarde, cuando aparezca el recolector de basura y llame a Finalizar, volverá a llamarDispose
.No solo es un desperdicio, sino que si su objeto tiene referencias basura a objetos que ya eliminó de la última llamada
Dispose()
, ¡intentará eliminarlos nuevamente!Notarás en mi código que tuve cuidado de eliminar las referencias a los objetos que he dispuesto, por lo que no trato de invocar
Dispose
una referencia de objeto basura. Pero eso no impidió que un error sutil se deslizara.Cuando el usuario llama
Dispose()
: se destruye el identificador CursorFileBitmapIconServiceHandle . Más tarde, cuando se ejecuta el recolector de basura, intentará destruir el mismo controlador nuevamente.La forma de solucionar esto es decirle al recolector de basura que no necesita molestarse en finalizar el objeto: sus recursos ya se han limpiado y no se necesita más trabajo. Para ello, llame
GC.SuppressFinalize()
alDispose()
método:Ahora que el usuario ha llamado
Dispose()
, tenemos:No tiene sentido que el GC ejecute el finalizador: todo se ha solucionado.
¿No podría usar Finalizar para limpiar recursos no administrados?
La documentación para
Object.Finalize
dice:Pero la documentación de MSDN también dice, para
IDisposable.Dispose
:Entonces cual es? ¿Cuál es el lugar para limpiar los recursos no administrados? La respuesta es:
Ciertamente, podría colocar su limpieza no administrada en el finalizador:
El problema con esto es que no tienes idea de cuándo el recolector de basura se encargará de finalizar tu objeto. Sus recursos nativos no administrados, innecesarios y no utilizados se quedarán hasta que el recolector de basura finalmente se ejecute. Entonces llamará a su método finalizador; limpiar recursos no administrados. La documentación de Object.Finalize señala esto:
Esta es la virtud de usar
Dispose
para limpiar recursos no administrados; puede saber y controlar cuándo se limpian los recursos no administrados. Su destrucción es "determinista" .Para responder a su pregunta original: ¿Por qué no liberar memoria ahora, en lugar de cuando el GC decida hacerlo? Tengo un software de reconocimiento facial que necesita deshacerse de 530 MB de imágenes internas ahora , ya que ya no son necesarias. Cuando no lo hacemos: la máquina se detiene por completo.
Lectura adicional
Para cualquiera a quien le guste el estilo de esta respuesta (explicando el por qué , por lo que el cómo se vuelve obvio), le sugiero que lea el Capítulo Uno del COM esencial de Don Box:
En 35 páginas, explica los problemas del uso de objetos binarios e inventa COM ante sus ojos. Una vez que se da cuenta del por qué de COM, las 300 páginas restantes son obvias y solo detallan la implementación de Microsoft.
Creo que cada programador que alguna vez haya tratado con objetos o COM debería, como mínimo, leer el primer capítulo. Es la mejor explicación de cualquier cosa.
Lectura extra de bonificación
Cuando todo lo que sabes está mal por Eric Lippert
fuente
null
. En primer lugar, significa que no puede hacerlosreadonly
y, en segundo lugar, debe hacer!=null
verificaciones muy feas (como en el código de ejemplo). Podría tener una banderadisposed
, pero es más fácil no preocuparse por ella. .NET GC es lo suficientemente agresivo como para que una referencia a un campox
ya no se cuente como 'usada' cuando pasa lax.Dispose()
línea.IDisposable
a menudo se usa para explotar lausing
declaración y aprovechar una manera fácil de hacer una limpieza determinista de objetos administrados.fuente
El propósito del patrón Dispose es proporcionar un mecanismo para limpiar los recursos administrados y no administrados y cuando eso ocurre depende de cómo se llame al método Dispose. En su ejemplo, el uso de Dispose no está haciendo nada relacionado con la eliminación, ya que borrar una lista no tiene ningún impacto en la eliminación de esa colección. Del mismo modo, las llamadas para establecer las variables en nulo tampoco tienen impacto en el GC.
Puede consultar este artículo para obtener más detalles sobre cómo implementar el patrón Dispose, pero básicamente se ve así:
El método más importante aquí es Dispose (bool), que en realidad se ejecuta en dos circunstancias diferentes:
El problema con simplemente dejar que el GC se encargue de hacer la limpieza es que no tienes control real sobre cuándo el GC ejecutará un ciclo de recolección (puedes llamar a GC.Collect (), pero realmente no deberías) para que los recursos se queden alrededor más de lo necesario. Recuerde, llamar a Dispose () en realidad no causa un ciclo de recolección o de ninguna manera hace que el GC recolecte / libere el objeto; simplemente proporciona los medios para una limpieza más determinista de los recursos utilizados y le dice al GC que esta limpieza ya se ha realizado.
El objetivo de IDisposable y el patrón de eliminación no se trata de liberar memoria inmediatamente. La única vez que una llamada a Eliminar tendrá incluso la posibilidad de liberar memoria de inmediato es cuando maneja el escenario de disposición == falso y manipula los recursos no administrados. Para el código administrado, la memoria no se recuperará hasta que el GC ejecute un ciclo de recopilación, sobre el cual realmente no tiene control (aparte de llamar a GC.Collect (), que ya he mencionado no es una buena idea).
Su escenario no es realmente válido ya que las cadenas en .NET no usan recursos no administrados y no implementan IDisposable, no hay forma de obligarlos a "limpiarse".
fuente
No debería haber más llamadas a los métodos de un objeto después de haber llamado a Dispose (aunque un objeto debería tolerar más llamadas a Dispose). Por lo tanto, el ejemplo en la pregunta es tonto. Si se llama Dispose, entonces el objeto en sí puede descartarse. Por lo tanto, el usuario debe descartar todas las referencias a ese objeto completo (establecerlas como nulas) y todos los objetos relacionados internos se limpiarán automáticamente.
En cuanto a la pregunta general sobre administrado / no administrado y la discusión en otras respuestas, creo que cualquier respuesta a esta pregunta tiene que comenzar con una definición de un recurso no administrado.
Todo se reduce a que hay una función a la que puede llamar para poner el sistema en un estado, y hay otra función a la que puede llamar para que vuelva a salir de ese estado. Ahora, en el ejemplo típico, la primera podría ser una función que devuelve un identificador de archivo, y la segunda podría ser una llamada a
CloseHandle
.Pero, y esta es la clave, podrían ser cualquier par de funciones coincidentes. Uno construye un estado, el otro lo derriba. Si el estado se ha construido pero aún no se ha derribado, existe una instancia del recurso. Debe organizar el desmontaje en el momento adecuado: el recurso no es administrado por el CLR. El único tipo de recurso administrado automáticamente es la memoria. Hay dos tipos: el GC y la pila. Los tipos de valores son administrados por la pila (o enganchando un viaje dentro de los tipos de referencia), y los tipos de referencia son administrados por el GC.
Estas funciones pueden causar cambios de estado que se pueden intercalar libremente, o pueden necesitar estar perfectamente anidadas. Los cambios de estado pueden ser seguros para subprocesos o no.
Mire el ejemplo en la pregunta de Justice. Los cambios en la sangría del archivo de registro deben estar perfectamente anidados, o todo sale mal. También es poco probable que sean seguras.
Es posible engancharse con el recolector de basura para limpiar sus recursos no administrados. Pero solo si las funciones de cambio de estado son seguras para subprocesos y dos estados pueden tener vidas que se superponen de alguna manera. ¡Entonces el ejemplo de Justice de un recurso NO debe tener un finalizador! Simplemente no ayudaría a nadie.
Para ese tipo de recursos, solo puede implementar
IDisposable
, sin un finalizador. El finalizador es absolutamente opcional, tiene que serlo. Esto se pasa por alto o ni siquiera se menciona en muchos libros.Luego debe usar la
using
declaración para tener alguna posibilidad de asegurarse de queDispose
se llame. Esto es esencialmente como enganchar un viaje con la pila (por lo que como finalizador es para el GC,using
es para la pila).La parte que falta es que tiene que escribir manualmente Dispose y hacer que llame a sus campos y a su clase base. Los programadores de C ++ / CLI no tienen que hacer eso. El compilador lo escribe para ellos en la mayoría de los casos.
Hay una alternativa, que prefiero para los estados que se anidan perfectamente y no son seguros (aparte de cualquier otra cosa, evitar IDisposable le ahorra el problema de tener una discusión con alguien que no puede resistirse a agregar un finalizador a cada clase que implementa IDisposable) .
En lugar de escribir una clase, escribes una función. La función acepta un delegado para volver a llamar a:
Y luego un ejemplo simple sería:
La lambda que se pasa sirve como un bloque de código, por lo que es como si hicieras tu propia estructura de control para cumplir el mismo propósito que
using
, excepto que ya no tienes ningún peligro de que la persona que llama abusa de ella. No hay forma de que puedan dejar de limpiar el recurso.Esta técnica es menos útil si el recurso es del tipo que puede tener vidas superpuestas, porque entonces desea poder construir el recurso A, luego el recurso B, luego matar el recurso A y luego matar el recurso B. No puede hacer eso si has obligado al usuario a anidar perfectamente así. Pero luego debe usarlo
IDisposable
(pero aún sin un finalizador, a menos que haya implementado threadsafety, que no es gratuito).fuente
enter
yexit
es el núcleo de cómo pienso en un recurso. Suscribirse / darse de baja en eventos debe encajar en eso sin dificultad. En términos de las características ortogonales / fungibles, es prácticamente indistinguible de una pérdida de memoria. (Esto no es sorprendente ya que la suscripción solo agrega objetos a una lista).Escenarios que utilizo IDisposable: limpiar recursos no administrados, cancelar la suscripción a eventos, cerrar conexiones
El idioma que uso para implementar IDisposable ( no threadsafe ):
fuente
Sí, ese código es completamente redundante e innecesario y no hace que el recolector de basura haga nada que de otra manera no haría (una vez que una instancia de MyCollection sale del alcance, eso es). Especialmente las
.Clear()
llamadas.Respuesta a su edición: más o menos. Si hago esto:
Es funcionalmente idéntico a esto para fines de administración de memoria:
Si realmente realmente necesita liberar la memoria en este mismo instante, llame
GC.Collect()
. Sin embargo, no hay razón para hacer esto aquí. La memoria se liberará cuando sea necesario.fuente
Si
MyCollection
se va a recolectar basura de todos modos, entonces no debería necesitar tirarla. Hacerlo simplemente agitará la CPU más de lo necesario e incluso puede invalidar algunos análisis precalculados que el recolector de basura ya ha realizado.Solía
IDisposable
hacer cosas como asegurarme de que los hilos estén dispuestos correctamente, junto con los recursos no administrados.EDITAR En respuesta al comentario de Scott:
Conceptualmente, el GC mantiene una vista del gráfico de referencia de objeto y todas las referencias a él desde los marcos de pila de hilos. Este montón puede ser bastante grande y abarcar muchas páginas de memoria. Como optimización, el GC almacena en caché su análisis de páginas que es poco probable que cambien con mucha frecuencia para evitar volver a escanear la página innecesariamente. El GC recibe una notificación del núcleo cuando los datos en una página cambian, por lo que sabe que la página está sucia y requiere un nuevo análisis. Si la colección está en Gen0, es probable que otras cosas en la página también estén cambiando, pero esto es menos probable en Gen1 y Gen2. Como anécdota, estos ganchos no estaban disponibles en Mac OS X para el equipo que transfirió el GC a Mac para que el complemento Silverlight funcionara en esa plataforma.
Otro punto contra la eliminación innecesaria de recursos: imagine una situación en la que un proceso se está descargando. Imagine también que el proceso se ha estado ejecutando durante algún tiempo. Lo más probable es que muchas de las páginas de memoria de ese proceso se hayan cambiado al disco. Por lo menos ya no están en la caché L1 o L2. En tal situación, no tiene sentido que una aplicación que se está descargando cambie todos esos datos y páginas de códigos de nuevo a la memoria para 'liberar' los recursos que el sistema operativo va a liberar de todos modos cuando el proceso finalice. Esto se aplica a los recursos administrados e incluso a ciertos recursos no administrados. Solo se deben eliminar los recursos que mantienen vivos los subprocesos que no son de fondo; de lo contrario, el proceso seguirá vivo.
Ahora, durante la ejecución normal, hay recursos efímeros que deben limpiarse correctamente (como @fezmonkey señala las conexiones de bases de datos, sockets, identificadores de ventanas ) para evitar pérdidas de memoria no administradas. Estos son los tipos de cosas que deben eliminarse. Si creas alguna clase que posee un hilo (y por dueño, quiero decir que lo creó y, por lo tanto, es responsable de garantizar que se detenga, al menos según mi estilo de codificación), entonces esa clase probablemente deba implementar
IDisposable
y derribar el hilo duranteDispose
..NET Framework usa la
IDisposable
interfaz como una señal, incluso una advertencia, para los desarrolladores de que esta clase debe eliminarse. No puedo pensar en ningún tipo de marco que implementeIDisposable
(excluyendo implementaciones de interfaz explícitas) donde la eliminación es opcional.fuente
Dispose()
llamadas opcionales , consulte: stackoverflow.com/questions/913228/…En el ejemplo que publicó, todavía no "libera la memoria ahora". Toda la memoria es basura recolectada, pero puede permitir que la memoria se recolecte en una generación anterior . Tendría que ejecutar algunas pruebas para estar seguro.
Las Pautas de diseño del marco son pautas y no reglas. Le indican para qué sirve principalmente la interfaz, cuándo usarla, cómo usarla y cuándo no.
Una vez leí el código que era un simple RollBack () en caso de falla utilizando IDisposable. La siguiente clase de MiniTx verificaría una marca en Dispose () y si la
Commit
llamada nunca sucedió, entonces se llamaríaRollback
a sí misma. Agregó una capa de indirección que hace que el código de llamada sea mucho más fácil de entender y mantener. El resultado se parecía a:También he visto que el código de tiempo / registro hace lo mismo. En este caso, el método Dispose () detuvo el temporizador y registró que el bloque había salido.
Así que aquí hay un par de ejemplos concretos que no hacen ninguna limpieza de recursos no administrada, pero sí usan IDisposable para crear un código más limpio.
fuente
Si desea eliminar ahora , use la memoria no administrada .
Ver:
fuente
No repetiré las cosas habituales sobre Usar o liberar recursos no administrados, todo eso ha sido cubierto. Pero me gustaría señalar lo que parece un error común.
Dado el siguiente código
Me doy cuenta de que la implementación Desechable no sigue las pautas actuales, pero espero que todos tengan la idea.
Ahora, cuando se llama a Dispose, ¿cuánta memoria se libera?
Respuesta: ninguna.
Llamar a Dispose puede liberar recursos no administrados, NO PUEDE reclamar memoria administrada, solo el GC puede hacerlo. Eso no quiere decir que lo anterior no sea una buena idea, de hecho, seguir el patrón anterior sigue siendo una buena idea. Una vez que se ha ejecutado Dispose, no hay nada que impida que el GC vuelva a reclamar la memoria que estaba utilizando _Large, a pesar de que la instancia de LargeStuff todavía puede estar dentro del alcance. Las cadenas en _Large también pueden estar en gen 0 pero la instancia de LargeStuff podría ser gen 2, por lo que nuevamente, la memoria se volvería a reclamar antes.
Sin embargo, no tiene sentido agregar un finalizador para llamar al método Dispose que se muestra arriba. Eso retrasará la recuperación de la memoria para permitir que se ejecute el finalizador.
fuente
LargeStuff
ha existido el tiempo suficiente para llegar a la Generación 2, y si_Large
contiene una referencia a una cadena recién creada que está en la Generación 0, entonces si la instancia deLargeStuff
se abandona sin anular_Large
, entonces la cadena a la que hace referencia_Large
se mantendrá hasta la próxima colección Gen2. La reducción a cero_Large
puede permitir que la cadena se elimine en la próxima colección Gen0. En la mayoría de los casos, anular referencias no es útil, pero hay casos en los que puede ofrecer algún beneficio.Además de su uso principal como una forma de controlar la vida útil de los recursos del sistema (¡completamente cubierto por la increíble respuesta de Ian , felicitaciones!), El combo IDisposable / using también se puede utilizar para determinar el cambio de estado de los recursos globales (críticos) : la consola , los hilos , el proceso , cualquier objeto global como una instancia de aplicación .
He escrito un artículo sobre este patrón: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Ilustra cómo puede proteger algunos estados globales de uso frecuente de una manera reutilizable y legible : colores de consola , cultura actual de hilos , propiedades de objetos de aplicaciones de Excel ...
fuente
En todo caso, esperaría que el código sea menos eficiente que cuando se omite.
Llamar a los métodos Clear () no es necesario, y el GC probablemente no lo haría si Dispose no lo hiciera ...
fuente
Hay cosas que la
Dispose()
operación hace en el código de ejemplo que podrían tener un efecto que no ocurriría debido a un GC normal delMyCollection
objeto.Si los objetos referenciados por
_theList
o_theDict
son referidos por otros objetos, entonces esoList<>
oDictionary<>
de objetos no estarán sujetos a la colección pero de repente no tendrá ningún contenido. Si no hubiera una operación Dispose () como en el ejemplo, esas colecciones aún contendrían su contenido.Por supuesto, si esta fuera la situación, lo llamaría un diseño roto; solo estoy señalando (pedagógicamente, supongo) que la
Dispose()
operación podría no ser completamente redundante, dependiendo de si hay otros usos delList<>
oDictionary<>
no se muestra en el fragmento.fuente
Un problema con la mayoría de las discusiones sobre "recursos no administrados" es que realmente no definen el término, pero parecen implicar que tiene algo que ver con el código no administrado. Si bien es cierto que muchos tipos de recursos no administrados interactúan con el código no administrado, pensar en recursos no administrados en esos términos no es útil.
En cambio, uno debería reconocer lo que todos los recursos administrados tienen en común: todos ellos implican un objeto que le pide a una 'cosa' externa que haga algo en su nombre, en detrimento de otras 'cosas', y la otra entidad acuerda hacerlo hasta que Aviso adicional. Si el objeto fuera abandonado y desapareciera sin dejar rastro, nada le diría a esa 'cosa' externa que ya no necesitaba alterar su comportamiento en nombre del objeto que ya no existía; en consecuencia, la utilidad de la cosa se reduciría permanentemente.
Un recurso no administrado, entonces, representa un acuerdo por parte de una 'cosa' externa para alterar su comportamiento en nombre de un objeto, lo que perjudicaría inútilmente la utilidad de esa 'cosa' externa si el objeto fuera abandonado y dejara de existir. Un recurso gestionado es un objeto que se beneficia de dicho acuerdo, pero que se ha registrado para recibir una notificación si se abandona, y que utilizará dicha notificación para poner sus asuntos en orden antes de que se destruya.
fuente
IDisposable
es bueno para darse de baja de eventos.fuente
Primero de la definición. Para mí, el recurso no administrado significa alguna clase, que implementa una interfaz IDisposable o algo creado con el uso de llamadas a dll. GC no sabe cómo lidiar con tales objetos. Si la clase tiene, por ejemplo, solo tipos de valor, entonces no considero esta clase como una clase con recursos no administrados. Para mi código sigo las siguientes prácticas:
siguiente muestra lo que describí en palabras como muestra de código:
fuente
is IDisposable
debería considerarse un recurso no administrado? Eso no parece correcto. Además, si el tipo implícito es un tipo de valor puro, parece sugerir que no es necesario eliminarlo. Eso también parece estar mal.Su ejemplo de código dado no es un buen ejemplo de
IDisposable
uso. La limpieza del diccionario normalmente no debería ir alDispose
método. Los elementos del diccionario se borrarán y eliminarán cuando salga del alcance.IDisposable
Se requiere la implementación para liberar algunos controladores / memoria que no se liberarán / liberarán incluso después de que estén fuera de alcance.El siguiente ejemplo muestra un buen ejemplo para el patrón IDisposable con algún código y comentarios.
fuente
El caso de uso más justificable para la eliminación de recursos gestionados es la preparación para que el GC recupere recursos que de otra forma nunca se recopilarían.
Un buen ejemplo son las referencias circulares.
Si bien es una buena práctica usar patrones que eviten referencias circulares, si termina con (por ejemplo) un objeto 'hijo' que tiene una referencia de nuevo a su 'padre', esto puede detener la colección GC del padre si simplemente abandona la referencia y confíe en GC: además, si ha implementado un finalizador, nunca se llamará.
La única forma de evitar esto es romper manualmente las referencias circulares estableciendo las referencias principales en nulas en los elementos secundarios.
Implementar IDisposable en padres e hijos es la mejor manera de hacerlo. Cuando se llama a Dispose en el Parent, llame a Dispose en todos los Children y, en el método Child Dispose, establezca las referencias Parent en null.
fuente
WeakReference
, el sistema verificará un indicador que indica que se encontró una referencia rooteada en vivo en el último ciclo GC , y agregará el objeto a una cola de objetos que necesitan una finalización inmediata, liberará el objeto del montón de objetos grandes o invalidará la referencia débil. Las referencias circulares no mantendrán vivos los objetos si no existen otras referencias.Veo que muchas respuestas se han desplazado para hablar sobre el uso de IDisposable para recursos administrados y no administrados. Sugeriría este artículo como una de las mejores explicaciones que he encontrado sobre cómo realmente se debe usar IDisposable.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Para la pregunta real; Si usa IDisposable para limpiar objetos administrados que ocupan mucha memoria, la respuesta corta sería no . La razón es que una vez que deseche un ID desechable, debería dejarlo fuera de alcance. En ese momento, los objetos secundarios a los que se hace referencia también están fuera del alcance y se recopilarán.
La única excepción real a esto sería si tiene mucha memoria atada a objetos administrados y ha bloqueado ese hilo esperando que se complete alguna operación. Si esos objetos no se necesitarían después de que se completara esa llamada, establecer esas referencias en nulo podría permitir que el recolector de basura las recolecte antes. Pero ese escenario representaría un código incorrecto que debía ser refactorizado, no un caso de uso de IDisposable.
fuente