2011-11-22 10 views
14

¿Hay algún análisis de escape realizado por el compilador CLR/JIT? Por ejemplo, en Java parece que una variable de bucle un objeto asignado en un bucle que no escapa al bucle se asigna en la pila en lugar del montón (ver Escape analysis in Java).Análisis de escape en .NET CLR VM

Para aclarar, en el ejemplo siguiente, ¿el compilador optimizará la asignación de montón de foo ya que nunca escapa del bucle?

class Foo 
{ 
    int number; 
    Foo(int number) { this.number = number; } 
    public override string ToString() { return number.ToString(); } 
} 

for (int i = 0; i < 10000000; i++) 
{ 
    Foo foo = new Foo(i); 
    Console.WriteLine(foo.ToString()); 
} 
+0

Típicamente, una variable de bucle es un tipo de valor, que consigue asignado en el pila (en el caso de un bucle). –

+0

Me refiero a una variable asignada dentro del cuerpo del bucle. Actualizaré la pregunta para aclarar. – SimonC

+0

¿Te refieres a la * variable *? o el * objeto *? (conceptualmente muy diferente) –

Respuesta

11

Si se refiere a la objeto (new Foo(i);), entonces mi entendimiento es que no: esto nunca se asigna en la pila; sin embargo, morirá en la generación cero, por lo que será muy eficiente de recolectar. No pretendo conocer cada rincón oscura y húmeda de la CLI, pero no conozco ningún escenario en C# que conduzca a un tipo de referencia administrado asignado a la pila (cosas como stackalloc realmente no contar, y son altamente específicos). Obviamente en C++ tienes algunas opciones más, pero luego no es una instancia administrada.

Curiosamente, en MonoTouch/AOT podría se puede recoger inmediatamente, pero esa no es la CLI VM principal (y es para un escenario muy específico).

En cuanto a la variable - que se general será en la pila (y volver a utilizarse para cada iteración del bucle) - pero podría no ser. Por ejemplo, si se trata de un "bloque iterador", todas las variables locales no eliminadas son en realidad campos en la máquina de estados generada por el compilador. Más comúnmente, si la variable es "capturada" (en un método anónimo o expresión lambda, que forman cierres), la variable se transforma en un campo en el contexto de captura generado por el compilador, y está separado por ciclo iteración (ya que foo está declarado dentro del ciclo). Esto significa que cada uno es por separado en el montón.

En cuanto a i (la variable de bucle) - si que es capturado, se pone aún más interesante

  • en C# no existían 1.2 capturas, sino por la especificación del bucle variable es técnicamente per-iteración
  • en C# 2,0 a 4,0, la variable de bucle es compartida (causando la infame captura/foreach pregunta común)
  • en C# 5,0 y por encima de la variable de bucle es per-iteración de nuevo

esto sólo se hace una diferencia cuando se captura la variable, pero cambia la semántica de exactamente cómo se manifiesta en la captura del contexto

1

Mientras que el JIT x86 es bueno en valuetypes 'inline', el fragmento no se califica como el método ToString será una llamada virtual en un objeto en caja. Editar: Esto puede no ser el caso, ya que no está anulando ToString.

Sin embargo, el x64 JIT no hace esto en absoluto a partir de mis experimentos.

Editar:

Si es posible, probar el código tanto en x86 y x64.

+1

'Foo' es una clase, así que no estoy seguro de cuánto de esto se aplica. –

5

Un tipo de valor puede asignarse en la pila (no siempre), pero lo mismo no es cierto para las instancias de los tipos de referencia. De hecho:

En particular, las ubicaciones de almacenamiento de las instancias de los tipos de referencia siempre se tratan como si fueran de larga vida, incluso si son de corta vida. Por lo tanto, siempre van en el montón.

(Eric Lippert: The Truth About Value Types)

también The Stack Is An Implementation Detail hace una buena lectura.