CLR /从32位进程切换到64位进程后的高内存消耗

时间:2014-07-31 21:53:39

标签: c# memory memory-management memory-leaks clr

我有一个基于.NET Framework 4.5(C#)构建的后端应用程序(Windows服务)。该应用程序在Windows Server 2008 R2服务器上运行,具有64GB内存。

由于我的依赖关系,我曾经编译并运行这个应用程序作为32位进程(编译为x86)并使用/ LARGEADDRESSAWARE标志让应用程序在用户空间中使用超过2GB的内存。使用此配置,平均内存消耗(根据任务管理器中的"内存(私有工作集)"列)大约为300-400MB。

我需要LARGEADDRESSAWARE标志的原因,以及我将其更改为64位的原因是,虽然300-400MB是平均值,但偶尔此应用会执行涉及加载的内容很多数据都存储在内存中(当你内存方式不是非常有限时,开发和管理这类内容会更容易)。

最近(在删除那些x86本机依赖项之后),我将应用程序编译更改为&#34;任何CPU&#34;,所以现在,在生产服务器上,它作为64位进程运行。从我做这个改变开始,平均内存消耗(根据任务管理器)达到了新的水平:3-4 GB ,当没有其他变化可以解释这种行为变化。< / p>

以下是有关当前状态的一些其他事实:

  • 根据&#34; #Bytes in all sheaps&#34;计数器,内存总量约为600MB。

  • 使用WinDbg + SOS调试进程时,!dumpheap -stat显示大约有250-300MB空闲,但所有其他对象远远小于进程使用的内存总量。

  • 根据GC性能计数器,定期有Gen0集合。实际上,GC中的&#34;%时间&#34; counter表示在GC上花费的时间平均为10-20%(考虑到应用程序的性质,这是很有意义的 - 很多短时间内使用的信息和数据结构的分配)。

  • 我在此应用中使用了Server GC。

  • 服务器上没有内存问题。它使用大约50-60%的可用内存(64GB)。

我的问题:

  • 为什么分配给进程的内存(根据任务管理器)和CLR堆的实际大小之间存在很大差异(流程中没有可以解释此问题的非托管代码)?

  • 与运行32位进程的进程相比,为什么64位进程需要更多内存?即使考虑到指针的大小只有两倍,也会有很大的不同。

  • 我能做些什么来降低内存消耗,或者更好地了解这个问题吗?

谢谢!

2 个答案:

答案 0 :(得分:3)

有几件事需要考虑:

1)您提到您正在使用服务器GC模式。在服务器GC模式下,CLR为机器上的每个CPU核心创建一个堆,这在服务器进程中更有效地进行多线程处理,例如, Asp.Net流程。每个堆都有两个段:一个用于小对象,一个用于大对象。每个段以4 gb保留内存开头。基本上,服务器GC模式尝试在系统上使用更多内存来交换整体系统性能。

2)当然,指针在64位上更大。

3)由于堆大得多,前台Gen2 GC在服务器GC模式下变得非常昂贵。因此,CLR尝试超级减少前景Gen2 GC的数量,有时使用后台Gen2 GC。

4)根据使用情况,碎片可能成为一个真正的问题。我已经看到了98%的碎片(98%的堆是免费的块)。

要真正解决您的问题,您需要获取ETW跟踪+内存转储,然后使用PerfView等工具进行详细分析。

答案 1 :(得分:0)

64位进程自然会使用64位指针,有效地将每个引用的内存使用量加倍。某些与平台相关的变量(如IntPtr)也会占用两倍的空间。

您可以做的第一件也是最好的事情是运行内存分析器,以查看确切的内存占用量来自何处。其他任何东西都是推测性的!