Pregunta

Me fascina la forma en que funciona el CLR y GC (estoy trabajando en ampliar mis conocimientos sobre esto mediante la lectura CLR a través de C #, libros / mensajes de Jon Skeet, y más).

De todos modos, ¿cuál es la diferencia entre decir:

MyClass myclass = new MyClass();
myclass = null;

O, al hacer MiClase implementar IDisposable y un destructor y llamar a Dispose ()?

Además, si tengo un bloque de código con una instrucción using (por ejemplo, más adelante), si me paso a través del código y salir del bloque usando, es el objeto desecharse después o cuando se produce una recogida de basura? ¿Qué pasaría si llamo a Dispose () en el bloque usando anyay?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

clases de flujo (por ejemplo BinaryWriter) tienen un método Finalize? ¿Por qué iba a querer usar eso?

¿Fue útil?

Solución

Es importante la eliminación por separado de recolección de basura. Son cosas completamente separadas, con un punto en común, que voy a venir a en un minuto.

Dispose, recolección de basura y la finalización

Cuando se escribe una declaración using, es simplemente azúcar sintáctica para un try / finally para que Dispose se llama incluso si el código en el cuerpo de la declaración using lanza una excepción. Es no significa que el objeto es basura recogida al final del bloque.

La eliminación es de aproximadamente recursos no administrados (recursos no memoria). Estas podrían ser las manijas de interfaz de usuario, conexiones de red, controladores de archivo, etc. Estos son recursos limitados, por lo que generalmente quieren liberarlos tan pronto como sea posible. Debe implementar IDisposable siempre que su tipo "posee" un recurso no administrado, ya sea directamente (por lo general a través de un IntPtr) o indirectamente (por ejemplo a través de un Stream, un SqlConnection etc).

La recolección de basura en sí es sólo alrededor de memoria - con un pequeño giro. El recolector de basura es capaz de encontrar objetos que ya no se puede hacer referencia, y liberarlos. No se ve para la basura todo el tiempo sin embargo - sólo cuando detecta que necesita (por ejemplo, si una "generación" de la pila se queda sin memoria)

.

El giro es finalización . El recolector de basura mantiene una lista de objetos que ya no son alcanzables, pero que tienen un finalizador (escrito como ~Foo() en C #, un tanto confusamente - no son nada como los destructores de C ++). Se ejecuta los finalizadores en estos objetos, por las dudas que tienen que hacer la limpieza adicional antes de que se liberó a su memoria.

Los finalizadores casi siempre se utilizan para limpiar los recursos en el caso de que el usuario del tipo se ha olvidado de disponer de ella de una manera ordenada. Por lo que si se abre un FileStream pero se olvida de llamar Dispose o Close, el finalizador será finalmente liberar el identificador de archivo subyacente para usted. En un programa bien escrito, finalizadores deben disparar casi nunca en mi opinión.

Configuración de una variable a null

Un pequeño punto sobre la configuración de una variable a null - esto casi nunca es necesario por el bien de la recolección de basura. Es posible que a veces desee hacerlo si es una variable miembro, aunque en mi experiencia es raro que "parte" de un objeto que se necesitaba más tiempo. Cuando se trata de una variable local, el JIT es por lo general lo suficientemente inteligente (en modo de lanzamiento) para saber cuándo usted no va a utilizar una referencia de nuevo. Por ejemplo:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

La única vez en que puede valer la pena establecer una variable local para null es cuando estás en un bucle, y algunas ramas del bucle necesita usar la variable pero se sabe que ha llegado a un punto en el que no lo hace. Por ejemplo:

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

Ejecución IDisposable / finalizadores

Por lo tanto, se deben poner en práctica sus propios tipos de finalizadores? Es casi seguro que no. Si sólo indirectamente poseen recursos no administrados (por ejemplo, usted tiene un FileStream como una variable miembro) a continuación, añadir su propio finalizador no ayudará: la corriente es casi seguro que sea elegible para la recolección de basura cuando su objeto es, por lo que sólo puede confiar en FileStream tener un finalizador (si es necesario - puede referirse a otra cosa, etc). Si desea mantener un recurso no administrado "casi" directamente, SafeHandle es su amigo - se necesita un poco de tiempo para ponerse en marcha con el, pero significa que va a casi nunca se necesita para escribir un finalizador de nuevo . Por lo general, debe necesitar solamente un finalizador si tiene un mango muy directo en un recurso (un IntPtr) y usted debe buscar para mover a SafeHandle tan pronto como sea posible. (hay dos enlaces no - leer tanto, a ser posible.)

Joe Duffy tiene una conjunto muy larga de directrices alrededor de finalizadores y IDisposable (co-escrito con un montón de gente inteligente) que vale la pena leer. Vale la pena estar consciente de que si usted sella sus clases, que hace la vida mucho más fácil:. El patrón de Dispose primordial para llamar a un nuevo método Dispose(bool) virtuales, etc sólo es relevante cuando su clase está diseñada para la herencia

Esto ha sido un poco de un paseo, pero por favor, pedir una aclaración donde desea alguna:)

Otros consejos

Cuando se deshaga un objeto, se liberan los recursos. Cuando se asigna a una variable nula, sólo estás cambiando una referencia.

myclass = null;

Después de ejecutar esto, el objeto miclase se refería a todavía existe, y continuará hasta que el GC consigue alrededor de la limpieza. Si Desechar se llama explícitamente, o sea en un bloque mediante, serán liberados los recursos tan pronto como sea posible.

Las dos operaciones no tienen mucho que ver entre otros. Cuando se establece una referencia a null, sino que simplemente lo hace. Es en sí mismo no afectará a la clase que se hace referencia en absoluto. La variable simplemente ya no apunta al objeto que solía, pero el objeto en sí no ha cambiado.

Cuando se llama a Dispose (), que es una llamada a un método en el objeto en sí. Cualquiera que sea el método Dispose hace, se hace ahora en el objeto. Pero esto no afecta a su referencia al objeto.

La única área de superposición es que cuando no hay más referencias a un objeto, será finalmente get basura recogida. Y si la clase implementa la interfaz IDisposable, a continuación, Eliminar () será llamado en el objeto antes de que consiga el recolector de basura.

Pero eso no ocurrirá inmediatamente después de establecer su referencia a nulo, por dos razones. En primer lugar, pueden existir otras referencias, por lo que no vamos a obtener de basura recogidas en absoluto, sin embargo, y en segundo lugar, incluso si esa fue la última referencia, por lo que ahora está listo para ser recogido de basura, no pasará nada hasta que el recolector de basura decide eliminar el objeto.

Llamar a Dispose () sobre un objeto no "matar" al objeto de ninguna manera. Se utiliza comúnmente para limpiar de manera que el objeto puede se eliminarán de forma segura después, pero en última instancia, no hay nada mágico sobre botar, es sólo un método de clase.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top