There's 一个老问题 https://stackoverflow.com/questions/1255803/does-the-net-clr-jit-compile-every-method-every-time/1255832每次询问 C# 是否都是 JIT 编译时,著名的 Jon Skeet 的回答是:“不,每个应用程序只编译一次”,只要我们谈论的是非 NGEN 的桌面应用程序。
我想知道 2009 年的信息是否仍然正确,并且我想通过实验和调试来弄清楚这一点,可能是通过在 JITter 上放置断点并使用 WinDbg 命令来检查对象和方法。
到目前为止我的研究
我知道 .NET 内存布局在实际数据开始(地址 A+4)之前考虑每个对象的标头(地址 A-4)和方法表(地址 A+0)。因此,每个对象可能有不同的方法表,因此可能有不同的 JITted 方法。
为什么我怀疑这个说法的正确性?
我们举办了一个并行编程研讨会,培训师声称每个线程的每个对象的方法都是 JITted 的。这显然对我来说没有意义,我能够编写一个反例应用程序。
不幸的是,出现了以下其他主题,我也想为此编写一个演示:
链接的答案是在 .NET 3.5 发布时编写的。从那时起它没有发生实质性变化,即它没有收到 .NET 4.0、4.6 和 4.6 的更新。
关于应用程序域,我个人的观点是,我可以卸载应用程序域,从而卸载程序集。如果卸载程序集,它就会消失,IL 代码也会随之消失。我认为保留被破坏的 IL 代码的本机代码没有多大好处。因此,我可以想象,创建应用程序域并再次加载程序集可能会导致再次对该方法进行 JIT 处理。
关于代码访问安全性,我不确定是JIT编译器根据当前权限考虑的还是运行时通过反射完成的。如果它是由 JIT 编译器完成的,则编译后的代码将会有所不同,具体取决于权限集。
JIT 的“设计原则”是编译一次方法(泛型方法的方法实例化)并重用相同的本机代码。当然,实现非常复杂,我将尝试在不影响准确性的情况下简化答案。对于从 .NET 2.0 到最新的 .NET 4.6 的所有运行时版本,答案都是相同的(我不知道 .NET 1.x,可能是相同的)。
运行时分析器回调和 ETW 事件的记录很少。当尝试 JIT 编译但不一定成功时,这两种情况都会发生。在三种情况下可能会发生这种情况:1-该方法无法满足某些安全要求,2-该方法的验证失败,3-无法分配内存来保存要发出的本机代码。因此,JIT 启动回调和事件可能会高估方法实际编译的次数。同样,JIT 完成回调和事件也不准确。在极少数情况下,他们可能会低估同一方法成功编译的次数。此时值得一提的是准确报告在进程的所有应用程序域中集体编译所有 IL 方法的次数。
对于特定于应用程序域的程序集,方法在每个应用程序域中单独编译。不存在共享(尽管有时在技术上是可行的)。对于与应用程序域无关的程序集,运行时attempts编译每个方法一次并与所有应用程序域共享本机代码。
如何证明 .NET CLR JIT 每个方法仅编译一次
跑步?
好吧,在某些情况下(例如使用后台 JIT 和其他极其微妙的情况时),方法可以在不执行的情况下进行编译。因此,说每个方法每次运行都会编译一次是不准确的。
您可以参考 CoreCLR JIT 源代码以获取更多信息(JIT 与 .NET Framework 4.5+ 中使用的 JIT 相同,但此答案适用于旧版本,因为 JIT 触发机制基本相同)。源代码就是证据。
每个线程的每个对象的方法都是 JITted
是的,这没有任何意义。编译范围是appdomains。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)