.NET 进程和 AppDomain 在啥情况下会共享内存中加载的程序集?

Posted

技术标签:

【中文标题】.NET 进程和 AppDomain 在啥情况下会共享内存中加载的程序集?【英文标题】:Under what circumstances will .NET processes and AppDomains share loaded assemblies in memory?.NET 进程和 AppDomain 在什么情况下会共享内存中加载的程序集? 【发布时间】:2015-09-26 03:12:10 【问题描述】:

我正在寻找有关 .NET 应用程序何时以及如何共享加载的程序集的更多详细信息。我有兴趣在操作系统进程之间共享,也对同一进程内的 AppDomain 之间共享感兴趣。共享程序集通过避免在内存中拥有同一程序集的多个副本来减少系统内存使用量,我认为这是主要好处,但有兴趣知道是否还有其他好处和/或影响。

到目前为止我所学的总结......

    Sysinternals process explorer 可用于列出 .NET 进程的 AppDomain 以及加载到每个 AppDomain 中的程序集。

    .NET 进程似乎总是将“核心”程序集加载到名为“SharedDomain”的 AppDomain 中(可以合理地假设这是在当前进程中的 AppDomain 之间共享的)。

    Task Manager 和 Process Explorer 报告“Working Set Shared”和“Working Set Shareable”的内存使用量并非微不足道,但不清楚共享的内容。 (它是共享 AppDomain 中的“核心”程序集吗?其他 [非核心] 程序集是否也共享?

    在一个简单的测试中,我启动了一个独立的 .NET 应用程序的两个副本,并为每个副本附加了一个 Visual Studio 调试器。 “模块”视图显示加载的程序集及其在内存中的地址。在我的测试用例中,每个加载的模块都位于两个进程中的相同地址。 (这是否表示共享,或者这个虚拟地址空间不一定是共享的?)

    ASP.NET 4.5 支持通过称为程序集实习的机制共享程序集(请参阅Look at Sharing Common Assemblies in ASP.NET 4.5、Sharing Common Assemblies、Sharing common assemblies with aspnet_intern.exe)。它似乎通过设置文件系统符号链接(符号链接)来工作,以便不同的 Web 应用程序指向一个共享的 bin 文件夹,因此这提出了 ASP.NET 是否只是使用符号链接来触发 .NET 中的标准程序集共享行为的问题,或者是否有更具体的 ASP.NET 和 IIS AppPools 正在进行。

注意。在安装了 Visual Studio 2013 的机器上,可以在以下位置找到 aspnet_intern.exe:

C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 工具\

在更高版本的 .NET 和 Windows Server 中对 ASP.NET 的启动时间和内存使用进行了进一步的改进;请参阅ASP.NET App Suspend – responsive shared .NET web hosting、Performance Improvements for ASP.NET Shared Hosting Scenarios in .Net 4.5,但我不确定这些更改与此问题的相关性如何。

Introducing .NET 4.5 一书也介绍了 ASP.NET 程序集共享。

还想知道 JITted 代码是否共享,因为加载的程序集由 MSIL、资源、元数据等组成,并且在 JITted 代码时必须分配更多内存。

还有关于紧凑框架中的程序集共享的讨论(We Believe in Sharing,MSDN Blogs,Abhinaba Basu)

---更新---

我使用sysinternals VMMap tool 检查了两个 AppPool,一个带有 asp.net 程序集内部设置,另一个没有。我还“触摸”了一个测试 aspx 页面以使 ASP.NET 加载所有程序集(并且 global.asax 运行少量代码,因此会导致一些 JITting)。

报告的两个 AppPool 的内存使用数据非常相似,Working Set、WS Private 和 WS Shareable 基本相同。然而,WS Shared 在“实习”AppPool 中要大得多。这是出乎意料的(对我来说),因为没有其他进程可以共享,但是 VMMap 显示了在实习 AppPool 中显示为共享内存的内存块(标记为“.text”并具有执行/读取保护),而另一个 AppPool 中的相同程序集不共享。我对此的解释是进程中的虚拟内存块被映射到相同的物理内存,然后报告为“WS Shared”。

ASLR

关于装配空间布局随机化。 VMMap 工具显示了许多具有“图像(ASLR)”类型的内存块。 ASLR 将程序集在内存中的位置随机化以阻止恶意软件,我想知道这是否会阻止程序集实习正常工作。使用EMET tool 为机器禁用 ASLR 确实会导致程序集地址更加规则,但不会更改报告的内存编号,因此它似乎不会影响程序集实习。值得注意的是,VMMap 仍然显示带有“ASLR”的图像,我怀疑这只是意味着程序集/图像被标记为支持/允许 ASLR,而不是 ASLR 有效。

【问题讨论】:

这篇博文信息量很大。它使用术语Domain Neutral Assemblies 来表示您所描述的(我相信)。 blogs.msdn.com/b/junfeng/archive/2004/08/05/208375.aspx 我投票决定重新打开,因为尽管问题文本中有很多切线相关的信息,但问题本身定义明确并且非常狭窄。很少有操作系统进程和/或 AppDomain 会与另一个进程共享程序集和内存的情况。 【参考方案1】:

发生程序集共享的一种情况是使用 ngen.exe 将程序集编译为本机代码。让我引用“通过 C# 实现 CLR”(第 1 章)

NGen.exe 工具在两种情况下很有趣:

...

减少应用程序的工作集 - 如果您认为程序集将同时加载到多个进程中,则在该程序集上运行 NGen.exe 可以减少应用程序的工作集。原因是 NGen.exe 工具将 IL 编译为本机代码并将输出保存在 单独的文件。这个文件可以被内存映射成多进程 同时地址空间,允许共享代码;不是 每个进程都需要自己的代码副本。

【讨论】:

以上是关于.NET 进程和 AppDomain 在啥情况下会共享内存中加载的程序集?的主要内容,如果未能解决你的问题,请参考以下文章

在啥情况下会在控制权到达 main() 函数之前发生崩溃? [复制]

memcpy在啥情况下会失败

memcpy在啥情况下会失败

React:useState 钩子中的 setState 在啥情况下会导致重新渲染?

SQLite中的页面缓存在啥情况下会被清除?

Linux信号函数的SIG_ERR在啥情况下会出现?