记录CLR JIT策略

3
我希望了解CLR在JIT编译中应用的范围和顺序。例如,如果我的应用程序只调用给定类的一个方法,那么该类的未使用方法是否会被不必要地JIT编译?如果是,它们是否在执行我所需的一个方法之前全部被JIT编译,还是在需要时才进行懒惰编译?
对于方法中的分支怎么办?CLR是否允许编译方法中一半的代码,同时允许同一方法中的另一个分支保持未编译状态,直到需要时再进行编译?
似乎随着时间的推移,我找到了一些提供这些细节的文章,但现在我找不到任何提供CLR如何选择JIT代码段的综合可读摘要。有什么建议的书籍或链接吗?
最好的情况是,这样的指南将按.NET版本将JIT决策逻辑进行分解。
1个回答

6
在.NET中,JIT的工作方式是,在方法被JIT之前,方法表条目指向一个小的存根,当调用时会JIT该方法。之后,方法表将更新为引用JIT编译代码的位置。
鉴于只有被调用的方法才会被JIT编译,因此对于未被调用的方法不存在JIT开销。
当需要时,JIT编译器将编译整个方法。如果是发布版本,则可能会优化掉代码,否则该方法将完全编译。
您可以使用WinDbg/SOS来检查方法表。请考虑以下内容:
class SomeType
{
    public void Called()
    {
        Console.WriteLine("called");
    }

    public void NotCalled()
    {
        Console.WriteLine("not called");
    }
}

假设我们创建了一个SomeType的实例,调用Called方法,然后检查SomeType的方法表。在x86平台上,你会看到类似这样的东西:
0:000> !dumpmt -md 00a7381c
EEClass:         00a712d0
Module:          00a72e94
Name:            ConsoleApplication1.SomeType
mdToken:         02000002
File:            c:\temp\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 7
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
72ca4960 729a6728 PreJIT System.Object.ToString()
72c98790 729a6730 PreJIT System.Object.Equals(System.Object)
72c98360 729a6750 PreJIT System.Object.GetHashCode()
72c916f0 729a6764 PreJIT System.Object.Finalize()
00df00d8 00a73814    JIT ConsoleApplication1.SomeType..ctor()
00df0110 00a737fc    JIT ConsoleApplication1.SomeType.Called()
00a7c031 00a73808   NONE ConsoleApplication1.SomeType.NotCalled()

注意到Called已经进行了JIT编译,但由于我们还没有调用NotCalled,因此它尚未进行JIT编译。
另外,请注意,来自object的方法都已经预编译。请记住,在发布版本中,短的方法可能会被内联,因此它们不会作为方法被调用,而只是作为呼叫站点生成的代码的一部分包含在内。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接