2012-06-27 6 views
7

Digamos que tiene una clase con una propiedad de evento. Si crea una instancia de esta clase en un contexto local, sin referencias externas, ¿asignaría una expresión lambda al evento para evitar que la instancia se convirtiera en basura?¿Las lambdas asignadas a un evento previenen la recolección de basura del objeto propietario?

{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
} 
// Will 'o' be eligible for garbage collection here? 
+1

Debe ser elegible para GC. No estoy seguro de esto para convertirlo en una respuesta. –

+0

No veo por qué esto se comportaría de manera diferente a la suscripción de cualquier otra función (lambda o no) a un evento. Así que estoy de acuerdo con Tim. –

+0

posible duplicado de [La recolección de basura cuando se utiliza delegados anónimos para el manejo de eventos] (http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling) –

Respuesta

5

No, o se liberarán, al igual que la función lambda. No hay referencias a o desde ningún otro lugar, por lo que no hay ninguna razón para que no se libere.

1

Cuando el tiempo de ejecución abandona ese bloque de código, ya no hay más referencias al objeto, por lo que se recolecta la basura.

+0

¿Cómo sabes que no hay más referencias en el almacenamiento local? –

+0

No importa si hay referencias a algo. De hecho, el lambda es probablemente un objeto asignado a un montón con una referencia a ese objeto, por lo que su reclamo es falso. Lo que le importa al GC es si un objeto es * alcanzable *. – delnan

0

Depende de qué quiere decir exactamente con "contexto local". Compare:

static void highMem() 
{ 
    var r = new Random(); 
    var o = new MyClass(); 
    o.MyClassEvent += a => { }; 
} 
static void Main(string[] args) 
{ 
    highMem(); 
    GC.Collect(); //yes, it was collected 
} 

Para:

static void Main(string[] args) 
{ 
    var r = new Random(); 
    { 
     var o = new MyClass(); 
     o.MyClassEvent += a => { }; 
    } 
    GC.Collect(); //no, it wasn't collected 
} 

En mi entorno (versión de depuración, VS 2010, Win7 x64), el primero de ellos era elegible para la GC y el segundo no era (como se comprobó por tener MyClass tomar 200 MB de memoria y comprobar que en el Administrador de tareas), a pesar de que estaba fuera de alcance. Supongo que esto se debe a que el compilador declara todas las variables locales al inicio de un método, por lo que para el CLR, o no está fuera del alcance, aunque no pueda usarlo en el código C#.

+1

'o' está fuera del alcance, pero el alcance tiene poco que ver con la recolección de elementos no utilizados, las referencias en vivo sí lo están. Si se usa un espacio de pila de referencia mientras una referencia está todavía en el alcance, se puede recopilar. Incluso se puede recopilar antes de que se finalicen las operaciones en él, en algunos casos. –

2

Respuesta corta, no, o serán liberados. No te preocupes por eso respuesta

Ligeramente más largo:

Su código hace más o menos lo siguiente:

  1. crear un poco de almacenamiento local en ese hilo para una referencia a los nuevos MiClase (o).
  2. Crea la nueva MyClass
  3. Guarda una referencia a la nueva MyClass en o.
  4. Crea un nuevo delegado de la lambda.
  5. Asigna que el delegado al evento (o ahora tiene una referencia al delegado).

En cualquier punto durante esto, el GC puede detener los hilos y luego examinar qué objetos están enraizados o no. Las raíces son objetos que están estáticos o en el almacenamiento local de un hilo (me refiero a las variables locales en la ejecución de un hilo dado, implementadas por una pila, en lugar de "Thread-Local Storage", que es esencialmente una forma de estática). Los objetos enraizados son raíces, objetos a los que hacen referencia, objetos a los que hacen referencia, etc. Los objetos enraizados no se recopilarán, el resto lo hará (salvo que se hagan algunas cosas extra con los finalizadores, lo ignoraremos por el momento).

En ningún momento hasta ahora después de la creación del objeto MyClass no ha sido rooteado por el almacenamiento local del hilo.

En ningún punto hasta ahora después de que el objeto delegado que se está creando no haya sido rooteado por el almacenamiento local del subproceso o por el hecho de que MyClass tenga una referencia al mismo.

Ahora, ¿qué pasa después?

Depende. La lambda no mantendrá viva la MyClass (la MyClass la mantiene viva, pero cuando la MyClass se va, también lo hace la lambda). Esto no es suficiente para responder "¿Serán elegibles para la recolección de basura aquí?" aunque.

void Meth0() 
{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
}//o is likely eligible for collection, though it doesn't have to be. 

void Meth1() 
{ 
    int i = 0; 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    }//o is likely not eligible for collection, though it could be. 
    while(i > 100000000);//just wasting time 
} 

void Meth2() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    int i = 0;//o is likely eligible for collection, though it doesn't have to be. 
    while(i > 100000000);//just wasting time 
    } 
} 

void Meth3() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    var x0 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x1 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x2 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x3 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x4 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x5 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x6 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x7 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x8 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x9 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x10 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x11 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    int i = 0; 
    while(i > 100000000);//just wasting time 
    } 
} 

Meth0 es aparentemente el más sencillo, pero en realidad no lo es, así que va a volver a ella.

En meth1, la aplicación es probable estar dejando el almacenamiento local tal como es, ya que no ha necesitado más, así que mientras o no está en el alcance, la aplicación tendrá aún que el almacenamiento local en uso.

En meth2, la implementación podría utilizar el mismo almacenamiento local que estaba utilizando para o para i así que, aunque todavía en su alcance, sería elegible para recibir ("alcance" quiere decir que el programador podría optar por hacer algo con eso, pero él o ella no y el código compilado no tiene necesidad de tal concepto).

Meth3 es más probable que vuelva a utilizar el almacenamiento, debido a que los usos temporales adicionales de la misma hacen que la aplicación frente dejando a un lado todo el almacenamiento, para empezar, tendría más sentido.

Ninguna de estas cosas tiene de esta manera. Meth2 y Meth3 podrían dejar de lado todo el almacenamiento necesario para el método al principio. Meth1 podría reutilizar el almacenamiento porque no tiene importancia reordenar i y o asignados.

Meth0 es más complicado, ya que podría depender de lo que el método de llamada hace junto con el almacenamiento local, en lugar de estar allí una limpieza en el momento (ambos son implementaciones legítimos). IIRC siempre hay una limpieza con la implementación actual, pero no estoy seguro y no importa de todos modos.

En total, el alcance no es lo relevante, pero si el compilador y más tarde el jitter puede y hace uso del almacenamiento local en relación con un objeto. Incluso es posible limpiar un objeto antes de que se invoquen los métodos y las propiedades (si esos métodos y propiedades no utilizan this o cualquiera de los campos del objeto, porque funcionará muy bien si el objeto se ha eliminado).)

Cuestiones relacionadas