La Pila (The Stack) y el Montículo (The Heap) en C# .NET

0
8186

Cuando un programa se va a ejecutar, se reserva un espacio de memoria para almacenar su entorno, así como también los datos que va a utilizar. La cantidad de memoria que será requerida, donde y como será almacenada, dependerá del tipo de dato a declarar.

Al momento de requerir un espacio de memoria, se puden usar dos áreas distintas, las cuales son administradas de forma diferente. Estas dos áreas de memoria se llaman: La Pila (The Stack) y El Montículo o Montón (The Heap). Cada una con diferentes propósitos.

The Stack

La Pila es un arreglo de memoria que esta ordenado mediante el método LIFO (Last-in, First-out), donde el último elemento en entrar es el primero en salir.

Las características generales de la Pila son las siguientes:

  1. Los datos solamente pueden ser añadidos o removidos desde la parte superior de la pila.
  2. Agregar un elemento a la pila se denomina Pushing.
  3. Remover un elemento de la pila se denomina Popping.
  4. La pila almacena:
    1. El entorno de ejecución del programa actual.
    2. Parámetros pasados a los métodos.
    3. Los valores de ciertos tipos de variables.

Otra forma de describir a La Pila es relacionándola con la forma en que está organizada una pila de cartones, los cuales se colocan uno encima del otro.

Por ejemplo, cuando se llama a un método, cada parámetro se coloca dentro de un cartón y luego el mismo se coloca en la parte superior de la pila. De la misma manera las variables locales del método, se colocan dentro de un cartón y luego se aplia sobre las cajas de parámetros existentes. Cuando el método finaliza, se remueven las cajas de variables y parámetros respectivamente.

El mismo ciclo de vida de los elementos almacenados en la pila ocurre cuando se declaran variables dentro de paréntesis, como por ejemplo en una sentencia While, For, etc.

Ejemplo 1

En el siguiente código del programa TheStackTest1 tipo consola, creamos tres variables:

namespace TheStackTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            int var1 = 20000;
            short var2 = 15;
            bool var3 = true;
        }
    }
}

Mediante las ventanas Call Stack y Locals de Visual Studio podemos ver la pila:

Como nos podemos dar cuenta, el primer elemento de la pila es el parámetro args del método Main, y el ultimo o el que se encuentra en la parte superior de la pila es la variable var3.

Para visualizar las ventana Call Stack y Locals, debemos ir al menú Debug, submenú Windows y elegir la opción Call Stack o Locals.

Ejemplo 2

En el siguiente código del programa TheStackTest2 tipo consola, creamos las mismas tres variables del Ejemplo 1, seguido de dos métodos más: Metodo1 y Metodo2.

using System;

namespace TheStackTest2
{
    class Program
    {
        static void Main(string[] args)
        {
            int var1 = 20000;
            short var2 = 15;
            bool var3 = true;

            Metodo1(var1, var2, var3);
        }

        static void Metodo1(int param1, short param2, bool param3)
        {
            decimal var4 = 5.05m;

            Metodo2(var4);
        }

        static void Metodo2(decimal param4)
        {
            string var5 = "FIN";

            Console.WriteLine(var5);
        }
    }
}

A continuación, se muestran las pilas de cada método:

Pila método 1.

Pila método 2.

Tal como se puede visualizar, al momento de llamar a un método, se añaden elementos a la pila (parámetros y variables) y una vez finalizados, se eliminan.

En estos ejemplos hemos usado tipos de datos predefinidos por C#, pero y ¿Si quisiéramos definir nuestras propias clases u objetos? Es aquí donde entra en juego el Montículo.

The Heap

El Montículo o Montón es un área de memoria donde se asignan fragmentos o espacios para almacenar ciertos tipos de objetos. A diferencia de la Pila, los datos pueden ser agregados y removidos en cualquier orden.

Las características generales del Montículo son las siguientes:

  1. Todo objeto es almacenado en el Montón, mientras la referencia al objeto está en la Pila.
  2. El tamaño del Montículo no es ilimitado, si al momento de crear un objeto no hay suficiente espacio, ocurrirá una excepción de tipo OutOfMemoryException, y el objeto no será creado.

Si tomamos nuevamente como referencia las cajas de cartón, en el caso del Montículo dichas cajas están esparcidas en el piso de una habitación sin un orden especifico.

Cuando un objeto es creado, el runtime busca una caja vacía del Montón de la habitación y lo coloca adentro. La referencia al objeto es almacenada en una variable local en la Pila. El runtime mantiene una pista del número de referencias a la caja donde se encuentra el objeto, ya que dos o más variables pueden hacer referencia a un mismo objeto. Cuando la última referencia al objeto desaparece, el runtime marca la caja como vacía o en desuso, y en este punto estará lista para que ese espacio de memoria quede disponible y se pueda almacenar otro objeto.

Los objetos almacenados en el Montículo no pueden ser eliminados de forma explícita. Por defecto, el Garbage Collector automáticamente limpia los objetos huérfanos cuando detecta que el código tiene un largo periodo de tiempo sin usarlos, con la ventaja que existan menos errores de memoria.

Observación

Cabe recalcar que el sistema se encarga automáticamente de la gestión de la Pila y el Montículo. Sin embargo, como desarrolladores es importante tener los conceptos claros de cómo funcionan estos mecanismos para la administración de datos en memoria.

Referencias

Ver la pila de llamadas y usar la ventana pila de llamadas del depurador