减less.NET应用程序的内存使用情况?

有什么提示可以减less.NET应用程序的内存使用量? 考虑以下简单的C#程序。

class Program { static void Main(string[] args) { Console.ReadLine(); } } 

x64的 发行模式下编译并在Visual Studio外部运行,任务pipe理器将报告以下内容:

 Working Set: 9364k Private Working Set: 2500k Commit Size: 17480k 

如果仅为x86编译,则会更好一些:

 Working Set: 5888k Private Working Set: 1280k Commit Size: 7012k 

然后,我尝试了下面的程序,它执行相同的操作,但在运行时初始化后尝试修剪进程大小:

 class Program { static void Main(string[] args) { minimizeMemory(); Console.ReadLine(); } private static void minimizeMemory() { GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetProcessWorkingSetSize(IntPtr process, UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize); } 

在Visual Studio之外的x86 版本上的结果:

 Working Set: 2300k Private Working Set: 964k Commit Size: 8408k 

哪一个更好一点,但对于这样一个简单的程序来说似乎仍然过分。 有没有什么窍门让C#过程变得更加精简? 我正在编写一个程序,大部分时间都是在后台运行。 我已经在一个单独的应用程序域中执行任何用户界面的东西,这意味着用户界面的东西可以安全地卸载,但是当它坐在后台时占用10MB似乎是过度的。

PS至于为什么我会在意—(电源)用户往往担心这些事情。 即使对性能几乎没有影响,半技术型用户(我的目标受众)往往会陷入后台应用程序内存使用的困境。 甚至当我看到Adobe Updater占用11MB的内存,并感受到Foobar2000的平静的抚摸,即使在播放时可能需要6MB以下的抚慰,我也会感到不适。 我知道在现代操作系统中,这些东西在技术上并不重要,但这并不意味着它不会对感知产生影响。

  1. 你可能想看看堆栈溢出问题的.NET EXE内存的足迹
  2. MSDN博客文章Working set!=实际内存占用量是关于解密工作集,进程内存以及如何对总内存消耗进行精确计算的全部内容。

我不会说你应该忽略你的应用程序的内存占用情况 – 显然,更小,更高效的趋势是可取的。 但是,你应该考虑你的实际需求是什么。

如果您正在编写一个标准的Windows Forms和WPF客户端应用程序,这些应用程序注定要在个人电脑上运行,并且很可能是用户在其中运行的主要应用程序,那么您可能会对内存分配缺乏足够的理解。 (只要全部被释放)。

但是,为了解决一些不要担心的问题:如果您正在编写Windows Forms应用程序,该应用程序将运行在terminal服务环境中,可能由10个,20个或更多用户使用的共享服务器上运行,则是,你绝对要考虑内存使用情况。 而且你需要保持警惕。 解决这个问题的最好方法是使用良好的数据结构devise,并遵循有关何时分配的最佳实践。

与本地应用程序相比,.NET应用程序将占用更大的空间,因为它们都必须在进程中加载​​运行时和应用程序。 如果你想要的东西真的很整洁,.NET可能不是最好的select。

但是请记住,如果您的应用程序大部分处于睡眠状态,那么必要的内存页面将被换出内存,因此在大多数情况下系统上的负担并不那么大。

如果你想保持足迹小,你将不得不考虑内存使用情况。 这里有几个想法:

  • 减less对象的数量,并确保不要超过需要的任何实例。
  • 请注意List<T>以及类似的types,在需要时可以将容量翻倍,因为它们可能会导致50%的浪费。
  • 您可以考虑使用值types而不是引用types来强制堆栈中的内存,但请记住,默认堆栈空间仅为1 MB。
  • 避免超过85000个字节的对象,因为它们将会到不被压缩的LOH,因此可能容易被分割。

这可能不是一个详尽无遗的清单,而只是一些想法。

在这种情况下,您需要考虑的一件事是CLR的内存成本。 每个.Net进程都会加载CLR,因此需要考虑内存因素。 对于这样一个简单/小型的程序来说,CLR的成本将主导你的内存占用。

build立一个实际的应用程序并查看与这个基线程序的成本相比较的成本会更具启发性。

本身没有具体的build议,但是你可以看看CLR Profiler (从Microsoft免费下载)。
一旦你安装了它,看看这个如何做的页面 。

从如何操作:

本教程向您展示了如何使用CLR Profiler工具来调查您的应用程序的内存分配configuration文件。 您可以使用CLR事件探查器来识别导致内存问题的代码,例如内存泄漏,垃圾收集过多或效率低下。

可能要查看“真实”应用程序的内存使用情况。

与Java类似,不pipe程序大小如何,运行时都会有一些固定的开销,但是在那之后内存消耗将会更加合理。

还有办法减less这个简单程序的私人工作集:

  1. NGEN你的申请。 这将从您的stream程中移除JIT编译成本。

  2. 训练您的应用程序使用MPGO 减less内存使用 ,然后NGEN它。

有很多方法来减less你的足迹。

有一件事你永远不得不忍受在.NET中是你的IL代码的本地图像的大小是巨大的

而且这个代码不能在应用程序实例之间完全共享。 即使是NGEN的组件也不完全是静态的,他们仍然有一些小部件需要JITting。

人们也倾向于编写阻止内存远远超过必要的代码。

一个经常见到的例子:拿一个Datareader,把内容加载到一个DataTable中,只是把它写入一个XML文件。 你可以很容易地遇到OutOfMemoryException。 OTOH,您可以使用XmlTextWriter并滚动查看数据库,在滚动数据库光标时发出XmlNodes。 这样,你只有当前的数据库logging和它在内存中的XML输出。 它永远不会(或不太可能)获得更高的垃圾收集代,因此可以重复使用。

同样的情况也适用于获取一些实例的列表,做一些事情(产生数千个新实例,这些实例可能会在某个地方引用),即使之后不需要它们,仍然可以引用所有内容,直到foreach。 显式地忽略input列表和临时副产品意味着,即使在退出循环之前,该内存也可以重用。

C#有一个很好的特性叫迭代器。 它们允许您通过滚动input来stream式传输对象,只保留当前实例,直到获得下一个实例。 即使使用LINQ,你仍然不需要保留所有的东西,只是因为你想过滤。

解决标题中的一般问题而不是具体问题:

如果你正在使用一个返回大量数据的COM组件(比如大的2xN数组),并且只需要一小部分,那么可以编写一个包装器COM组件,该组件将隐藏.NET中的内存,并只返回数据需要。

这是我在我的主要应用程序中所做的,并显着改善了内存消耗。

我发现使用SetProcessWorkingSetSize或EmptyWorkingSet API在长时间运行的进程中定期强制内存页面到磁盘,可能会导致计算机上的所有可用物理内存有效消失,直到重新启动计算机。 我们有一个.NET DLL加载到本地进程中,在使用EmptyWorkingSet API(替代使用SetProcessWorkingSetSize)来减less执行内存密集型任务后的工作集。 我发现,在1天到1周之间的任何地方,机器将在任务pipe理器中显示99%的物理内存使用率,而没有任何进程显示使用任何显着的内存使用。 机器不久之后不会响应,需要重新启动。 所述机器是在物理和虚拟硬件上运行的二十多台Windows Server 2008 R2和2012 R2服务器。

也许将.NET代码加载到本地进程中有一些问题,但使用EmptyWorkingSet(或SetProcessWorkingSetSize)需要您自担风险。 也许只能在应用程序初次启动后使用它。 我决定禁用代码,并让垃圾收集器自己pipe理内存使用情况。