一个 .NET 程序首先被编译成 MSIL 代码。当它被执行时,JIT 编译器将它编译成本机机器码。
我想知道:
这些 JIT 编译的机器码存储在哪里?它只存储在进程的地址空间中吗?但由于程序的第二次启动比第一次快得多,我认为这些本机代码必须被存储在某个地方的磁盘上,即使执行已经完成。但是在哪里呢?
内存。它可以被缓存,这是ngen.exe的工作。它生成程序集的.ni.dll版本,包含机器代码并存储在全局程序集缓存中。此后自动加载,跳过JIT步骤。
但这与为什么您的程序第二次启动更快有很少关系。第一次您有所谓的“冷启动”。这完全被花费在硬盘上查找DLLs的时间所主导。第二次您有所谓的“热启动”,DLL已经在文件系统缓存中可用。
磁盘很慢。一个SSD是一个显而易见的解决方案。
值得注意的是:这不是仅适用于托管代码的问题。具有许多DLLs的大型非托管程序也会出现此问题。两个经典示例,几乎所有开发机器上都有的是Microsoft Office和Acrobat Reader。它们有一个技巧。安装时,它们将一个“优化器”放在Run注册表键或Startup文件夹中。这些优化器所做的只是加载主程序使用的所有DLL,然后退出。这将预热文件系统缓存,当用户随后使用该程序时,它将快速启动,因为它的热启动很快。
就个人而言,我发现这非常令人恼火。因为它们真正做的是减慢我可能想要在登录后启动的其他程序。这很少是Office或Acrobat。我会刻意删除这些优化器,如果必要的话反复删除当可恶的更新把它放回去时。
您也可以使用这个技巧,但请负责任地使用。
正如其他人指出的那样,在你的情况下,代码是按进程基础进行 JIT 编译的,而且不会被缓存 - 你在第二次加载时看到的加速是操作系统磁盘缓存(即内存中的)程序集所致。
然而,在桌面/服务器版本的 .NET Framework 中,除了操作系统磁盘缓存之外,没有缓存 JIT 编译后的机器代码;但在 .NET Framework 的另一个版本中却有缓存。
有趣的是,在 .NET Compact Framework(用于 Windows Phone 7 的 NETCF 版本)中发生了什么。最近的进展看到一些 JIT 后的框架代码在进程之间共享,其中 JIT 编译后的代码确实被缓存。这主要是为了在像移动电话这样的受限设备上提高性能(负载时间和内存使用情况)。
因此,回答这个问题,CLR 桌面/服务器版本中没有直接缓存 JIT 编译后的代码,但在最新版本的紧凑框架即 NETCF 中将有缓存。
参考: We Believe in Sharing
我相信即时编译的代码永远不会存储或交换出内存。您在第二次执行汇编时感受到的性能提升是由于依赖程序集已经存在于内存或磁盘缓存中。
exe
的Native Image
? - Sreekumar P