Dale la vuelta a tu pregunta. La verdadera pregunta motivadora es ¿en qué circunstancias podemos evitar los costos de la recolección de basura?
Bueno, en primer lugar, ¿cuáles son los costos de la recolección de basura? Hay dos costos principales. Primero, debes determinar qué está vivo ; eso requiere potencialmente mucho trabajo. En segundo lugar, debe compactar los agujeros que se forman cuando libera algo que se asignó entre dos cosas que aún están vivas. Esos agujeros son derrochadores. Pero compactarlos también es costoso.
¿Cómo podemos evitar estos costos?
Claramente, si puede encontrar un patrón de uso de almacenamiento en el que nunca asigne algo de larga duración, luego asigne algo de corta duración, luego asigne algo de larga duración, puede eliminar el costo de los agujeros. Si puede garantizar que para algún subconjunto de su almacenamiento, cada asignación posterior tenga una vida más corta que la anterior en ese almacenamiento, entonces nunca habrá agujeros en ese almacenamiento.
Pero si hemos resuelto el problema del agujero , también hemos resuelto el problema de la recolección de basura . ¿Tienes algo en ese almacenamiento que todavía está vivo? Sí. ¿Se asignó todo antes de que dure más? Sí, esa suposición es cómo eliminamos la posibilidad de agujeros. Por lo tanto, todo lo que necesita hacer es decir "¿está viva la asignación más reciente?" y sabes que todo está vivo en ese almacenamiento.
¿Tenemos un conjunto de asignaciones de almacenamiento donde sabemos que cada asignación posterior tiene una vida más corta que la asignación anterior? ¡Sí! Los marcos de activación de los métodos siempre se destruyen en el orden opuesto al que se crearon porque siempre tienen una vida más corta que la activación que los creó.
Por lo tanto, podemos almacenar marcos de activación en la pila y saber que nunca necesitan ser recolectados. Si hay algún fotograma en la pila, el conjunto completo de fotogramas debajo tiene una vida más larga, por lo que no es necesario recopilarlos. Y serán destruidos en el orden opuesto al que fueron creados. El costo de la recolección de basura se elimina así para los marcos de activación.
Es por eso que tenemos el grupo temporal en la pila en primer lugar: porque es una manera fácil de implementar la activación del método sin incurrir en una penalización de administración de memoria.
(Por supuesto, el costo de recolección de basura de la memoria a la que se refieren las referencias en los marcos de activación todavía está allí).
Ahora considere un sistema de flujo de control en el que los marcos de activación no se destruyen en un orden predecible. ¿Qué sucede si una activación de corta duración puede dar lugar a una activación de larga duración? Como puede imaginar, en este mundo ya no puede usar la pila para optimizar la necesidad de recolectar activaciones. El conjunto de activaciones puede contener agujeros nuevamente.
C # 2.0 tiene esta característica en forma de yield return
. Un método que produce un rendimiento de rendimiento se reactivará más adelante, la próxima vez que se llame MoveNext, y cuando eso suceda no es predecible. Por lo tanto, la información que normalmente estaría en la pila para el marco de activación del bloque iterador se almacena en el montón, donde se recolecta basura cuando se recopila el enumerador.
Del mismo modo, la función "async / await" que viene en las próximas versiones de C # y VB le permitirá crear métodos cuyas activaciones "ceden" y "reanudan" en puntos bien definidos durante la acción del método. Dado que los marcos de activación ya no se crean y destruyen de manera predecible, toda la información que solía almacenarse en la pila tendrá que almacenarse en el montón.
Es solo un accidente de la historia que decidimos por algunas décadas que los idiomas con marcos de activación que se crean y destruyen de manera estrictamente ordenada estaban de moda. Dado que los idiomas modernos carecen cada vez más de esta propiedad, espere ver más y más idiomas que reifiquen las continuaciones en el montón de basura recolectada, en lugar de la pila.