C#JIT编译和.NET


86

我对JIT编译器的工作原理有些困惑。我知道C#可以编译为IL。第一次运行它是准时的。这是否涉及将其转换为本地代码?.NET运行时(作为虚拟机吗?)是否与JIT代码交互?我知道这很幼稚,但我真的很困惑。我的印象一直是.NET运行时不解释程序集,但我不了解交互的详细信息。

Answers:


83

是的,JIT的IL代码涉及将IL转换为本地机器指令。

是的,在某种意义上,.NET运行时与JIT的本机代码交互,因为该运行时拥有本机代码占用的内存块,运行时调用本机代码等。

您是正确的.NET运行时不会解释程序集中的IL代码。

当执行到达尚未将JIT编译为本机代码的功能或代码块(如if块的else子句)时,会发生什么,调用JIT'r将IL的该块编译为本机代码。完成后,程序执行将输入刚发出的机器代码以执行其程序逻辑。如果在执行该本地机器代码时执行了对尚未编译为机器代码的函数的函数调用,则会调用JIT'r以“及时”编译函数。等等。

JIT'r不一定将功能主体的所有逻辑立即编译为机器代码。如果函数具有if语句,则只有在执行实际通过该语句块之前,if或else子句的语句块才可以进行JIT编译。未执行的代码路径在执行之前将保持IL形式。

编译后的本机代码将保存在内存中,以便下次代码部分下次执行时可以再次使用。第二次调用该函数将比第一次调用更快,因为第二次无需执行JIT步骤。

在桌面.NET中,本机代码在appdomain的生存期内保留在内存中。在.NET CF中,如果应用程序内存不足,则可能会丢弃本机代码。下次执行该代码时,将从原始IL代码再次对其进行JIT编译。


14
轻微的pedantic更正-“ JIT'r不一定编译功能主体的所有逻辑”-在可能是这种情况的理论实现中,但在.NET运行时的现有实现中,JIT编译器将编译整个方法全部一次。
保罗·亚历山大

18
至于为什么这样做,主要是因为非jit编译器无法假定某些优化可在任何给定的目标平台上使用。唯一的选择是编译为最低公分母,或更普遍的是,编译多个版本,每个版本针对自己的平台。JIT消除了这一缺点,因为最终转换为机器代码是在目标机器上完成的,目标机器在该机器上编译器知道哪些优化可用。
克里斯·谢恩

1
克里斯,甚至认为JIT知道可用的优化,但没有时间应用任何重要的优化。NGEN可能更强大。
Grigory 2013年

2
@Grigory是的,NGEN拥有JIT编译器所没有的时间,但是JIT可以做出很多代码生成决定来提高编译后的代码的性能,而这并不需要花费很多时间。例如,JIT编译器可以选择指令集以与运行时找到的可用硬件最匹配,而无需在JIT编译步骤中花费大量时间。我不认为.NET JITter会以任何重要方式进行此操作,但是有可能。
dthorpe

24

代码被“编译”为Microsoft中间语言,类似于汇编格式。

双击可执行文件时,Windows将加载mscoree.dll该文件,然后设置CLR环境并启动程序的代码。JIT编译器开始读取程序中的MSIL代码,然后将代码动态编译为x86指令,CPU可以执行该指令。


1

.NET使用一种称为MSIL的中间语言,有时缩写为IL。编译器读取您的源代码并生成MSIL。当您运行该程序时,.NET即时(JIT)编译器将读取MSIL代码并在内存中生成一个可执行应用程序。您不会看到任何这种情况,但是了解幕后情况是一个好主意。


1

我将通过以下示例将IL代码编译为本地CPU指令。

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

最初,CLR知道有关类型的所有详细信息以及由于元数据而从该类型中调用什么方法。

当CLR开始在本地CPU指令中执行IL时,CLR会为Main代码所引用的每种类型分配内部数据结构。

在我们的例子中,我们只有一个类型的Console,因此CLR将通过该内部结构分配一个内部数据结构,我们将管理对引用类型的访问

在该数据结构中,CLR包含该类型定义的所有方法的条目。每个条目都包含可以找到该方法的实现的地址。

初始化此结构时,CLR会将每个条目设置在CLR本身内部包含的未记录的FUNCTION中。您可以猜测到,此FUNCTION 是我们所谓的JIT编译器。

总的来说,您可以将JIT编译器视为CLR函数,它将IL编译为本地CPU指令。让我详细向您展示此过程在我们的示例中将如何进行。

1.Main首次调用WriteLine时,将调用JITCompiler函数。

2.JIT编译器函数知道正在调用什么方法以及定义此方法的类型。

3.然后,Jit编译器将在定义该类型的程序集中进行搜索,并在我们的WriteLine方法的IL代码中获取由该类型定义的方法的IL代码。

4.JIT编译器分配DYNAMIC内存块,然后JIT验证并将IL代码编译为本地CPU代码并将该CPU代码保存在该内存块中。

5.然后,JIT编译器返回内部数据结构条目,并将地址(主要参考WriteLine的IL代码实现)替换为地址新动态创建的内存块,其中包含WriteLine的本机CPU指令。

6.最后,JIT编译器功能跳转到内存块中的代码。这段代码是WriteLine方法的实现。

7.执行WriteLine之后,代码返回到市电代码,该代码将继续正常执行。


0

.NET Framework使用CLR环境生成MSIL(Microsoft中间语言),也称为IL。编译器读取您的源代码,并在您构建/编译项目时生成MSIL。现在,当您最终运行项目时,.NET JIT(即时编译器)就会开始起作用。JIT读取您的MSIL代码并生成可以由CPU轻松执行的本机代码(x86指令)。JIT读取所有MSIL指令并逐行执行它们。

如果您有兴趣看看,幕后发生了什么,已经得到解答。请关注-这里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.