Actualización (1 de diciembre de 2009):
Me gustaría enmendar esta respuesta y reconocer que la respuesta original era defectuosa.
El análisis original no se aplica a los objetos que requieren finalización - y el punto de que las prácticas no deben ser aceptados en la superficie sin una precisa, la comprensión en profundidad sigue en pie.
Sin embargo, resulta que DataSets, DataViews, DataTables suprimen la finalización en sus constructores ; es por eso que llamar a Dispose () en ellos explícitamente no hace nada.
Presumiblemente, esto sucede porque no tienen recursos no administrados; así que a pesar de que MarshalByValueComponent tiene en cuenta los recursos no administrados, estas implementaciones particulares no tienen la necesidad y, por lo tanto, pueden renunciar a la finalización.
(Que los autores de .NET se encargarían de suprimir la finalización en los tipos que normalmente ocupan más memoria habla de la importancia de esta práctica en general para los tipos finalizables).
No obstante, que estos detalles aún están poco documentados desde el inicio de .NET Framework (hace casi 8 años) es bastante sorprendente (que esencialmente se le deja a sus propios dispositivos para tamizar material aunque conflictivo y ambiguo para juntar las piezas). a veces es frustrante, pero proporciona una comprensión más completa del marco en el que confiamos todos los días).
Después de mucha lectura, aquí está mi entendimiento:
Si un objeto requiere finalización, podría ocupar la memoria más de lo necesario; he aquí por qué: a) Cualquier tipo que define un destructor (o hereda de un tipo que define un destructor) se considera finalizable; b) En la asignación (antes de que se ejecute el constructor), se coloca un puntero en la cola de Finalización; c) Un objeto finalizable normalmente requiere que se reclamen 2 colecciones (en lugar del estándar 1); d) La supresión de la finalización no elimina un objeto de la cola de finalización (según lo informado por! FinalizeQueue en SOS) Este comando es engañoso; Saber qué objetos están en la cola de finalización (en sí mismo) no es útil; Sería útil saber qué objetos están en la cola de finalización y aún requieren finalización (¿hay un comando para esto?)
Suprimir la finalización se apaga un poco en el encabezado del objeto, lo que indica al tiempo de ejecución que no necesita que se invoque su Finalizador (no es necesario mover la cola FReachable); Permanece en la cola de Finalización (y continúa siendo informado por! FinalizeQueue en SOS)
Las clases DataTable, DataSet, DataView están todas enraizadas en MarshalByValueComponent, un objeto finalizable que puede (potencialmente) manejar recursos no administrados
- Debido a que DataTable, DataSet, DataView no introducen recursos no administrados, suprimen la finalización en sus constructores
- Si bien este es un patrón inusual, libera a la persona que llama de tener que preocuparse por llamar a Descartar después de usar
- Esto, y el hecho de que DataTables potencialmente se pueden compartir entre diferentes DataSets, es probable que los DataSets no deseen deshacerse de DataTables secundarios.
- Esto también significa que estos objetos aparecerán debajo de! FinalizeQueue en SOS
- Sin embargo, estos objetos deben ser recuperables después de una sola colección, como sus contrapartes no finalizables.
4 (nuevas referencias):
Respuesta original
Hay muchas respuestas engañosas y generalmente muy pobres sobre esto: cualquiera que haya aterrizado aquí debe ignorar el ruido y leer cuidadosamente las referencias a continuación.
Sin lugar a dudas, Dispose se debe invocar en cualquier objeto Finalizable.
Las tablas de datos son finalizables.
Calling Dispose acelera significativamente la recuperación de memoria.
MarshalByValueComponent llama a GC.SuppressFinalize (this) en su Dispose (); omitir esto significa tener que esperar docenas, si no cientos de colecciones Gen0 antes de recuperar la memoria:
Con esta comprensión básica de la finalización, ya podemos deducir algunas cosas muy importantes:
Primero, los objetos que necesitan finalización viven más que los objetos que no. De hecho, pueden vivir mucho más. Por ejemplo, supongamos que un objeto que está en gen2 necesita ser finalizado. La finalización se programará pero el objeto todavía está en gen2, por lo que no se volverá a recopilar hasta que ocurra la próxima colección gen2. Eso podría ser mucho tiempo y, de hecho, si las cosas van bien, será mucho tiempo, porque las colecciones gen2 son costosas y, por lo tanto, queremos que sucedan con poca frecuencia. Los objetos más antiguos que necesitan finalización podrían tener que esperar docenas, si no cientos de colecciones gen0 antes de recuperar su espacio.
En segundo lugar, los objetos que necesitan finalización causan daños colaterales. Dado que los punteros de objetos internos deben permanecer válidos, no solo los objetos que necesitan finalización permanecen en la memoria sino que todo lo que el objeto hace referencia, directa e indirectamente, también permanecerá en la memoria. Si un gran árbol de objetos estuviera anclado por un solo objeto que requiriera finalización, entonces todo el árbol se demoraría, potencialmente durante mucho tiempo, como acabamos de comentar. Por lo tanto, es importante utilizar los finalizadores con moderación y colocarlos en objetos que tengan la menor cantidad posible de punteros internos. En el ejemplo del árbol que acabo de dar, puede evitar fácilmente el problema moviendo los recursos que necesitan finalización a un objeto separado y manteniendo una referencia a ese objeto en la raíz del árbol.
Finalmente, los objetos que necesitan finalización crean trabajo para el hilo finalizador. Si su proceso de finalización es complejo, el único hilo finalizador pasará mucho tiempo realizando esos pasos, lo que puede causar una acumulación de trabajo y, por lo tanto, provocar que más objetos permanezcan esperando la finalización. Por lo tanto, es de vital importancia que los finalizadores hagan el menor trabajo posible. Recuerde también que, aunque todos los punteros de objetos permanecen válidos durante la finalización, puede ser el caso de que esos punteros conduzcan a objetos que ya se hayan finalizado y, por lo tanto, no sean útiles. Por lo general, es más seguro evitar seguir punteros a objetos en el código de finalización aunque los punteros sean válidos. Una ruta de código de finalización corta y segura es la mejor.
Tómelo de alguien que ha visto cientos de MB de tablas de datos no referenciadas en Gen2: esto es muy importante y las respuestas en este hilo lo han perdido por completo.
Referencias
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry
http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/