Dentro de la maquina virtual de .net
Hola amig@s,
En este post voy a hacer una revisión a nivel de pila de lo que sucede en nuestra máquina virtual de .net y el CLR cuando llevamos a cabo una serie de acciones bastante frecuentes.
Antes que nada, comentaros que he optado por usar ejemplos de código en C#, lo cual espero que me perdonen aquellos adeptos de VB.net, jeje.
En primer lugar, vamos a ver cuál es el estado inicial de la pila de ejecución de un programa justo antes de invocar a un método que hemos denominado M1:
Una vez que comienza la ejecución de M1, su prólogo reservará espacio en la pila para las variables locales, como podemos ver a continuación:
En la siguiente instrucción de M1, se invoca a otro método, M2, pasándole la variable local "name", creada anteriormente, como argumento. Podemos observar que también se apila la dirección de retorno, es decir, la dirección a la que deberá volver el PC (contador de programa) cuando finalice la ejecución del método M2:

Al comenzar la ejecución del método M2, su prólogo reserva espacio para las variables locales del método en la pila, de manera análoga a lo que previamente hizo el método M1.

Al finalizar la ejecución de M2, se devuelve el control al método M1, haciendo uso de la dirección de retorno que le habíamos indicado anteriormente.

Finalmente, M1 finaliza su ejecución y devuelve el control al método desde el que había sido invocado.

Aspectos particulares del CLR:
Consideremos la siguiente declaración...
internal class Employee {
public Int32 GetYearsEmployed () {...}
public virtual String GetProgressReport () {...}
public static Employee Lookup (String name) {...}
}
internal sealed class Manager : Employee {
public override String GetProgressReport () {...}
}
Un programa en ejecución está a punto de llamar a M3, la pila y el heap ya han sido creados.

El compilador Just-In-Time (de ahora en adelante, JIT) compila M3 a código nativo. Se consultan las cabeceras de los ensamblados de los tipos Employee, Int32, Manager y String, y se crean las estructuras para representarlos (no se muestran las de Int32 y String):

Todos los objetos del heap contienen al menos dos miembros: un puntero a un objeto de tipo (type object) y un índice usado para sincronización de hilos de ejecución (sync bloc index). Los objetos de tipo Employee contienen, por tanto, ambos. También incluyen espacio para los campos estáticos de cada tipo y una tabla con los métodos definidos.
Ya está todo listo, sigamos con la ejecución del código nativo M3... 

El prólogo de M3 reserva espacio en la pila para las variables locales, CLR las inicializa a NULL o a cero automáticamente.
Se crea en el heap una instancia del tipo Manager. Este objeto contiene todas las variables de instancia del tipo Manager y de todas sus clases base.

CLR inicializa un puntero al tipo del objeto y todas las variables de instancia a NULL o a cero. Después se llama al constructor del tipo y, finalmente, se guarda en la variable 'e' la referencia al objeto.

Llamada a un método estático
Consideremos el siguiente código:
internal class Employee {
public Int32 GetYearsEmployed () {...}
public virtual String GetProgressReport () {...}
public static Employee Lookup (String name) {...}
}
internal sealed class Manager : Employee {
public override String GetProgressReport () {...}
}
CLR busca en la tabla del tipo Employee el método Lookup. El método se compila (si es necesario) con el compilador JIT y se invoca el código resultante.

Supongamos que Lookup construye un nuevo objeto de tipo Manager en el heap y devuelve su dirección. El primer objeto Manager se convierte en un objeto obvio para el recolector de basura...
Llamada a un método de instancia no virtual
Sea el siguiente código de declaración:
internal class Employee {
public Int32 GetYearsEmployed () {...}
public virtual String GetProgressReport () {...}
public static Employee Lookup (String name) {...}
}
internal sealed class Manager : Employee {
public override String GetProgressReport () {...}
}
El CLR buscaría en el objeto de tipo Employee, por ser el tipo de 'e'. Si Employee no definiera el método, iría descendiendo por la jerarquía de tipos buscándolo.
Llamada a un método virtual
Dada la siguiente declaración:
internal class Employee {
public Int32 GetYearsEmployed () {...}
public virtual String GetProgressReport () {...}
public static Employee Lookup (String name) {...}
}
internal sealed class Manager : Employee {
public override String GetProgressReport () {...}
}
El CLR seguiría la dirección de 'e' hasta el objeto del heap y desde éste llega al tipo real del objeto, Manager, y al objeto.

Si el método Lookup hubiera devuelto un objeto del tipo Employee, uno de los punteros habría sido distinto y la llamada a GetProgressReport habría ejecutado la implementación de Employee, en lugar de la de Manager.
Los punteros a objetos de tipo de los propios objetos de tipo apuntan a un objeto especial creado para el tipo System.Type. Los objetos de tipo Manager y Employee son "instancias" de este tipo.

El método GetType de System.Object devuelve la dirección almacenada en el puntero a objeto del tipo del objeto correspondiente. Así puede determinarse el tipo real de cada objeto.
Y por hoy nada más, que sino te ahogas 
Espero que haya sido de vuestro agrado, y gracias a los que habéis aguantado hasta aquí... que no es poco!!
Un saludo 