.NET: Leak en Windows Forms

Consideremos este código dentro de un Form:

int i, j;
for (i = 0; i < 150; i++)
{
    for (j = 0; j < 200; j++)
    {
        this.Controls.Add(new Button());
    }
    this.Text = String.Format("i={0}", i);
    this.Controls.Clear();
}

Tal vez haya que cambiar 150 y 200 por algo más grande en tu sistema, pero eventualmente esto explotará diciendo algo del estilo "Cannot create window handle". Esto es porque el Clear() no libera instantáneamente los botones; el GC debería hacerlo eventualmente, pero parece que no llega a tiempo.

¿A qué se debe esto? En primer lugar, debemos considerar que Windows Forms está montado sobre la API de Win32, así que detrás de esto hay un bucle donde se procesan mensajes (message pump). En segundo lugar, debemos considerar que en Win32, una ventana sólo puede ser destruida por el mismo thread que la creó. Por lo tanto, el Garbage Collector, al destruir los controles, debe despachar la llamada al thread creador. Y esto lo hace con la famosa cola de mensajes. Por lo tanto, si esa cola se bloquea por alguna razón, los controles no son liberados.

Por lo tanto, nos quedan dos opciones: asegurarnos de que la cola no se bloquee, o desbloquearla forzosamente. Lo segundo es mucho más fácil, se hace así:

int i, j;
for (i = 0; i < 150; i++)
{
    for (j = 0; j < 200; j++)
    {
        this.Controls.Add(new Button());
    }
    this.Text = String.Format("i={0}", i);
    this.Controls.Clear();
    Application.DoEvents();
}

Como es mucho más fácil, tiene su contra: Application.DoEvents() es un proceso costoso y debe usarse con moderación.

¿Y si hacemos esto con WPF, que supuestamente no usa Win32 y es posta posta?

for (var i = 0; i < 150; i++)
{
   for (var j = 0; j < 200; j++)
   {
        mGrid.Children.Add(new Button());
   }
   mGrid.Children.Clear();
}

¡No se cuelga jamás! Así que si es viable, sería mejor pasarse a WPF. Lo cual debería ser trivial si la GUI está bien aislada del modelo del negocio.

Referencias: Post en foro

Comments

Popular posts from this blog

VB.NET: Raise base class events from a derived class

Apache Kafka - I - High level architecture and concepts

Upgrading Lodash from 3.x to 4.x