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

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

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

我需要LARGEADDRESSAWARE标志的原因,以及将其更改为64位的原因是,尽管平均为300-400MB,但此应用偶尔执行的工作涉及将大量数据加载到内存中(并且当您在内存方面不是很有限时,开发和管理这类东西就容易得多了.

最近(除去那些x86本机依赖项之后),我将应用程序编译更改为“ Any CPU”,因此,现在,在生产服务器上,它作为64位进程运行.从我进行此更改开始,平均内存消耗(根据任务管理器)达到了新的水平:3-4 GB,此时没有其他更改可以解释此行为更改.

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

>根据“所有堆中的#Bytes”计数器,内存总量约为600MB.
>使用WinDbg SOS调试进程时,!dumpheap -stat显示大约有250-300MB的可用空间,但是所有其他对象都小于该进程使用的内存总量.
>根据GC性能指标,有定期的Gen0集合.实际上,“%GC时间”计数器表示平均花费在GC上的时间为10-20%(考虑到应用程序的性质,这很有意义-大量用于信息和数据结构的分配短时间).
>我在此应用中使用Server GC.
>服务器上没有内存问题.它使用约50-60%的可用内存(64GB).

我的问题:

>为什么分配给进程的内存(根据任务管理器)与CLR堆的实际大小(在进程中没有非托管代码可以解释这一点)之间有很大的区别?
>为什么与32位进程运行的同一个进程相比,64位进程占用更多的内存?即使考虑到指针的大小是其两倍,也存在很大差异.
>我可以做些降低内存消耗或更好地了解此问题的事情吗?

谢谢!

解决方法:

有几件事情要考虑:

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

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

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

4)根据使用情况,碎片化可能成为一个现实问题.我见过堆有98%的碎片(98%的堆是空闲块).

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

上一篇:C#-多态类上的Func-eval


下一篇:CLR内部异常(下)