JIT-компилятор (Just in time)
Для выполнения какого-либо метода, его IL-код должен быть преоразован в машинные команды. Этим и занимается JIT-компилятор.
Непосредственно перед исполнением метода Main среда CLR находит все типы данных, на которые ссылается программный код метода Main. При этом CLR выделяет внутренние структуры данных, используемые для управления доступом к типам, на которые есть ссылки. На рисунке ниже метод Main ссылается на единственный тип — Console, и среда CLR выделяет единственную внутреннюю структуру. Эта внутренняя структура данных содержит по одной записи для каждого метода, определенного в типе Console. Каждая запись содержит адрес, по которому можно найти реализацию метода. При инициализации этой структуры CLR заносит в каждую запись адрес внутренней недокументированной функции, содержащейся в самой среде CLR. Я обозначаю эту функцию JITCompiler.
Когда метод Main первый раз обращается к методу WriteLine, вызывается функция JITCompiler. Она отвечает за компиляцию IL-кода вызываемого метода в собственные команды процессора. Поскольку IL-код компилируется непосредственно перед выполнением («just in time»), этот компонент CLR часто называют JIT-компилятором.
Функции JITCompiler известен вызываемый метод и тип, в котором он определен. JITCompiler ищет в метаданных соответствующей сборки IL-код вызываемого метода. Затем JITCompiler проверяет и компилирует IL-код в машинные команды, которые сохраняются в динамически выделенном блоке памяти. После этого JITCompiler возвращается к структуре внутренних данных типа, созданной средой CLR, и заменяет адрес вызываемого метода адресом блока памяти, содержащего готовые машинные команды. В завершение JITCompiler передает управление коду в этом блоке памяти. Этот программный код является реализацией метода WriteLine (вариант этого метода с параметром String). Из этого метода управление возвращается в метод Main, который продолжает выполнение в обычном порядке. Рассмотрим повторное обращение метода Main к методу WriteLine. К этому моменту код метода WriteLine уже проверен и скомпилирован, так что обращение к блоку памяти производится напрямую, без вызова JITCompiler. Отработав, метод WriteLine возвращает управление методу Main. На рис. 1.5 показано, как выглядит ситуация при повторном обращении к методу WriteLine.
JIT-компилятор хранит машинные команды в динамической памяти. Это значит, что скомпилированный код уничтожается по завершении работы приложения. Для повторного вызова приложения или для параллельного запуска его второго экземпляра (в другом процессе операционной системы) JIT-компилятору придется заново скомпилировать IL-код в машинные команды.
Возможности повышения проиводительности упр. кода по сравнению с неуправляемым:
- JIT-компилятор может определить на каком процессоре выполянется приложение и использовать его специальные команды для повышения эффективности
- JIT-компилятор может определить, что некоторое условие на том компьютере, на котором он выполняется, всегда оказывается ложным
- CLR может профилировать выполняемую программу и перекомпилировать IL в машинный код в процессе выполнения. Перекомпилированный код реорганизуется для сокращения ошибочного прогнозирования переходов на основании наблюдаемых закономерностей выполнения.