【发布时间】:2011-03-18 18:14:40
【问题描述】:
.NET 程序首先被编译成 MSIL 代码。当它被执行时,JIT 编译器会将其编译为本机机器码。
我想知道:
这些 JIT 编译的机器代码存储在哪里?它是否仅存储在进程的地址空间中?但是由于程序的第二次启动比第一次要快得多,所以我认为即使在执行完成之后,这个本机代码也必须存储在磁盘上的某个地方。但是在哪里?
【问题讨论】:
.NET 程序首先被编译成 MSIL 代码。当它被执行时,JIT 编译器会将其编译为本机机器码。
我想知道:
这些 JIT 编译的机器代码存储在哪里?它是否仅存储在进程的地址空间中?但是由于程序的第二次启动比第一次要快得多,所以我认为即使在执行完成之后,这个本机代码也必须存储在磁盘上的某个地方。但是在哪里?
【问题讨论】:
简而言之,IL 是针对程序的每次调用进行 JIT 编译的,并在进程地址空间的代码页中进行维护。请参阅Richter 的第 1 章,详细了解 .NET 执行模型。
【讨论】:
我相信 JIT 编译的代码永远不会被存储或换出内存。您在第二次执行程序集时感受到的性能提升是由于相关程序集已经在内存或磁盘缓存中。
【讨论】:
每次第一次执行方法时,JIT 编译的机器代码都会缓存在每个方法的内存中。我认为它从未缓存到磁盘。
您可能会发现该进程在第二次加载时更快,因为 Windows 在第一次运行时缓存(在内存中)您的进程使用的文件(dll、资源等)。在第二次运行时无需转到磁盘,这可能在第一次运行时已完成。
您可以通过running NGen.exe 确认这一点,以便实际为您的架构预编译机器代码,并比较第一次和第二次运行的性能。我敢打赌,由于操作系统中的缓存,第二次运行仍然会更快。
【讨论】:
正如其他人所指出的,在您的情况下,代码是基于每个进程的 JIT,并且没有缓存 - 您在第二次加载时看到的加速是操作系统磁盘缓存(即内存中)程序集。
但是,虽然在桌面\服务器版本的框架中没有缓存(除了 OS 磁盘缓存),但在另一个版本的框架中存在 JIT 机器代码的缓存。
感兴趣的是 .Net Compact Framework(Windows Phone 7 版本的 NETCF)中发生的事情。最近的进展是在确实缓存了 JIT 代码的进程之间共享了一些 JIT 框架代码。这主要是为了在手机等受限设备中获得更好的性能(加载时间和内存使用)。
因此,在回答这个问题时,CLR 的桌面\服务器版本中没有 JIT 代码的直接框架缓存,但最新版本的紧凑框架(即 NETCF)中会有。
参考:我们相信分享
http://blogs.msdn.com/b/abhinaba/archive/2010/04/28/we-believe-in-sharing.aspx
【讨论】:
内存。它可以被缓存,这是 ngen.exe 的工作。它生成程序集的 .ni.dll 版本,包含机器代码并存储在 GAC 中。之后会自动加载,绕过 JIT 步骤。
但这与您的程序第二次启动更快的原因无关。第一次你有一个所谓的“冷启动”。这完全取决于在硬盘上查找 DLL 所花费的时间。第二次热启动时,DLL 已在文件系统缓存中可用。
磁盘很慢。 SSD 是一个显而易见的解决方案。
Fwiw:这不是托管代码独有的问题。具有大量 DLL 的大型非托管程序也有它。大多数开发机器上都有两个典型的例子是 Microsoft Office 和 Acrobat Reader。他们作弊。安装后,他们会在 Run 注册表项或 Startup 文件夹中放置一个“优化器”。这些优化器所做的只是加载主程序使用的所有 DLL,然后退出。这会初始化文件系统缓存,当用户随后使用该程序时,它将快速启动,因为它的热启动速度很快。
就我个人而言,我觉得这非常烦人。因为他们真正所做的是减慢我在登录后可能想要启动的任何其他程序。这很少是 Office 或 Acrobat。我强调删除这些优化器,如果有必要,当爆炸更新将其恢复时重复。
你也可以使用这个技巧,但请负责任地使用它。
【讨论】:
"It generates a .ni.dll version of the assembly, containing machine code and stored in the GAC"。我有一些疑问.. GAC 仅用于存储公共程序集,对吗?它是否存储了我正在运行的exe 的Native Image?
dir c:\windows\assembly 以列出 GAC 中的目录。
是的,NGEN.EXE 会将 JIT 编译版本的 .NET 可执行文件放在 GAC 中,即使当 MSIL 版本不存在。我已经尝试过了,但无济于事。 我相信,除非原始 MSIL 版本也在 GAC 中并且会被加载 从那里开始,将不再使用 GAC 中的 JIT 版本。 我也相信即时 JIT 编译(not NGEN)永远不会被缓存。他们占据进程 仅记忆。
我从阅读 MS 文档和各种实验中相信这一点。我也欢迎 那些“知道”的人对我的断言的确认或反驳。
【讨论】: