c# compiler-construction clr jit

c# - CLR vs JIT



compiler-construction (7)

¿Cuál es la diferencia entre el compilador JIT y CLR? Si compila su código para il y CLR ejecuta ese código, ¿qué está haciendo el JIT? ¿Cómo ha cambiado la compilación de JIT con la adición de genéricos al CLR?


  1. Common Language Runtime (CLR) es un intérprete mientras que Just In Time (JIT) es un compilador en .Net Framework.

2.JIT es el compilador interno de .NET que toma el código de MicroSoft Intermediate Code Language (MSICL) de CLR y lo ejecuta en instrucciones específicas de la máquina, mientras que CLR funciona como motor. Su tarea principal es proporcionar código MSICL a JIT para garantizar que el código sea completamente compilado según la especificación de la máquina.


1) al compilar el programa .net, el código de programa .net se convierte en código de Idioma Intermedio (IL)

2) al ejecutar el programa, el código de idioma intermedio se convierte en código nativo del sistema operativo cuando se llama un método; esto se llama compilación JIT (Just in Time).


Como dice Jon Skeet, JIT es parte del CLR. Básicamente esto es lo que está sucediendo bajo el capó:

  1. Su código fuente está compilado en un código de bytes conocido como el lenguaje intermedio común (CIL).
  2. Los metadatos de cada clase y cada método (y cualquier otra cosa: O) se incluyen en el encabezado PE del ejecutable resultante (ya sea un dll o un exe).
  3. Si está produciendo un ejecutable, el encabezado PE también incluye un programa de arranque convencional que se encarga de cargar el CLR (Common Language Runtime) cuando ejecuta el archivo ejecutable.

Ahora, cuando ejecutas:

  1. El bootstraper inicializa el CLR (principalmente al cargar el ensamblaje mscorlib) y le indica que ejecute su ensamblaje.
  2. CLR ejecuta su entrada principal.
  3. Ahora, las clases tienen una tabla vectorial que contiene las direcciones de las funciones del método, de modo que cuando se llama a MyMethod, se busca en esta tabla y luego se realiza una llamada correspondiente a la dirección. Al inicio TODAS las entradas para todas las tablas tienen la dirección del compilador JIT.
  4. Cuando se realiza una llamada a uno de dichos métodos, se invoca el JIT en lugar del método real y toma el control. El JIT luego compila el código CIL en un código de ensamblaje real para la arquitectura apropiada.
  5. Una vez que se compila el código, el JIT entra en la tabla de vectores de métodos y reemplaza la dirección con la del código compilado, de modo que cada llamada subsiguiente ya no invoque el JIT.
  6. Finalmente, el JIT maneja la ejecución al código compilado.
  7. Si llama a otro método que todavía no se ha compilado, regrese a 4 ... y así sucesivamente ...

El JIT es básicamente parte de la CLR. El recolector de basura es otro. El lugar donde pones responsabilidades de interoperabilidad, etc. es otro asunto, y otro en el que estoy muy poco calificado para comentar :)


El JIT es un aspecto del CLR.

Específicamente, es la parte responsable de cambiar CIL / MSIL (en lo sucesivo, denominado IL) producido por el compilador del lenguaje original (csc.exe para Microsoft c # por ejemplo) en código de máquina nativo del procesador actual (y la arquitectura que expone en el proceso actual) , por ejemplo 32 / 64bit). Si el conjunto en cuestión fue ngen''d entonces el proceso JIT es completamente innecesario y el CLR ejecutará este código sin problemas.

Antes de utilizar un método que aún no se haya convertido de la representación intermedia, es responsabilidad del JIT convertirlo.
Exactamente cuando el JIT se activará es específico de la implementación y está sujeto a cambios. Sin embargo, el diseño de CLR exige que el JIT ocurra antes de que se ejecute el código relevante, en cambio las JVM tendrían la libertad de interpretar el código por un tiempo, mientras que un hilo separado crea una representación de código de máquina.
El CLR ''normal'' utiliza un método de apéndice JIT previo mediante el cual los métodos se compilan JIT solo a medida que se utilizan. Esto implica que el trozo de método nativo inicial sea un indirecto para indicar al JIT que compile el método y luego modifique la llamada original para omitir el trozo inicial. La edición compacta actual en su lugar compila todos los métodos en un tipo cuando se carga.

Para abordar la adición de genéricos.

Este fue el último cambio importante en la especificación IL y el JIT en términos de su semántica en comparación con sus detalles de implementación interna.

Se agregaron varias nuevas instrucciones de IL, y se proporcionaron más opciones de metadatos para instrumentar tipos y miembros. Las restricciones se agregaron a nivel de IL también.

Cuando el JIT compila un método que tiene argumentos genéricos (explícita o implícitamente a través de la clase contenedora) puede configurar diferentes rutas de código (instrucciones de código de máquina) para cada tipo utilizado. En la práctica, el JIT utiliza una implementación compartida para todos los tipos de referencia, ya que las variables para estos exhibirán la misma semántica y ocuparán el mismo espacio (IntPtr.Size).

Cada tipo de valor obtendrá un código específico generado para él, tratar con el tamaño reducido / aumentado de las variables en la pila / montón es una razón importante para esto. Además, al emitir el código de operación restringido antes de las llamadas al método, muchas invocaciones en tipos no de referencia no necesitan incluir el valor para llamar al método (esta optimización también se usa en casos no genéricos). Esto también permite que el comportamiento <T> predeterminado se maneje correctamente y que las comparaciones con null se eliminen como no ops (siempre falso) cuando se usa un tipo de valor que no admite valores NULL.

Si se realiza un intento en el tiempo de ejecución para crear una instancia de un tipo genérico mediante reflexión, entonces los parámetros de tipo serán validados por el tiempo de ejecución para garantizar que pasen cualquier restricción. Esto no afecta directamente al JIT a menos que se use dentro del sistema de tipo (aunque es poco probable).


Sé que el hilo es bastante viejo, pero pensé que podría poner la imagen que me hizo entender JIT. Es del excelente libro CLR via C # de Jeffrey Ritcher . En la imagen, los metadatos de los que habla son los metadatos emitidos en el encabezado del ensamblaje donde se almacena toda la información sobre los tipos del ensamblaje:


Usted compila su código para IL que se ejecuta y compila en código de máquina durante el tiempo de ejecución, esto es lo que se llama JIT.

Editar , para darle más importancia a la respuesta (aún demasiado simplificada):

Cuando compila su código C # en Visual Studio, se convierte en IL que CLR entiende, el IL es el mismo para todos los idiomas que se ejecutan sobre el CLR (que es lo que permite que el tiempo de ejecución de .NET use varios idiomas e interopera entre ellos fácilmente).

Durante el tiempo de ejecución, el IL se interpreta en código máquina (que es específico de la arquitectura en la que se encuentra) y luego se ejecuta. Este proceso se llama compilación Just In Time o JIT para abreviar. Solo el IL que se necesita se transforma en código máquina (y solo una vez, se "almacena en caché" una vez que se compila en código máquina), justo a tiempo antes de que se ejecute, de ahí el nombre JIT.

Esto es lo que se vería para C #

C # Code > C # Compiler > IL > .NET Runtime > JIT Compiler > Machinecode > Execution

Y esto es lo que se vería para VB

Código VB > Compilador VB > IL > .NET Runtime > Compilador JIT > Código máquina > Ejecución

Y como puede ver, solo los dos primeros pasos son únicos para cada idioma, y ​​todo después de que se ha convertido en IL es el mismo que es, como dije antes, la razón por la que puede ejecutar varios idiomas diferentes encima de .NET