Reducing memory usage of .NET applications?

What are some tips to reduce the memory usage of .NET applications? Consider the following simple C# program.

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

Compiled in release mode for x64 and running outside Visual Studio, the task manager reports the following:

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

It's a little better if it's compiled just for x86:

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

I then tried the following program, which does the same but tries to trim process size after runtime initialization:

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);
}

The results on x86 Release outside Visual Studio:

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

Which is a little better, but it still seems excessive for such a simple program. Are there any tricks to make a C# process a bit leaner? I'm writing a program that's designed to run in the background most of the time. I'm already doing any user interface stuff in a separate Application Domain which means the user interface stuff can be safely unloaded, but taking up 10 MB when it's just sitting in the background seems excessive.

P.S. As to why I would care --- (Power)users tend to worry about these things. Even if it has nearly no effect on performance, semi-tech-savvy users (my target audience) tend to go into hissy fits about background application memory usage. Even I freak when I see Adobe Updater taking 11 MB of memory and feel soothed by the calming touch of Foobar2000, which can take under 6 MB even when playing. I know in modern operating systems, this stuff really doesn't matter that much technically, but that doesn't mean it doesn't have an affect on perception.

79875 次浏览

本身没有具体的建议,但是您可以看看 CLR 分析器(从 Microsoft 免费下载)。
Once you've installed it, take a look at this 怎么呼叫.

指南:

This How To shows you how to use the CLR Profiler tool to investigate your 应用程序的内存分配 您可以使用 CLR 事件探查器来 识别导致内存的代码 问题,例如内存泄漏和 过多或低效的垃圾 收藏品。

.NET 应用程序比本机应用程序占用的空间更大,因为它们都必须在进程中加载运行时和应用程序。如果你想要干净整洁的东西。NET 可能不是最好的选择。

However, keep in mind that if you application is mostly sleeping, the necessary memory pages will be swapped out of memory and thus not really be that much of a burden on the system at large most of the time.

如果您希望保持较小的内存占用量,则必须考虑内存使用情况。这里有一些想法:

  • 减少对象的数量,并确保保持任何实例的时间不超过所需的时间。
  • 请注意 List<T>和类似的类型,双倍的能力时,需要,因为他们可能会导致50% 的浪费。
  • 您可以考虑在引用类型之上使用值类型来强制堆栈上增加内存,但是请记住,默认的堆栈空间只有1MB。
  • 避免使用超过85000字节的对象,因为它们会转到 LOH,而 LOH 没有被压缩,因此很容易被分割。

无论如何,这可能不是一个详尽的清单,但只是一些想法。

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

构建一个真正的应用程序并查看其成本与这个基准程序的成本之间的比较,将更具有指导意义。

Might want to look at the memory usage of a "real" application.

与 Java 类似,无论程序大小如何,运行时的开销都是固定的,但是在此之后内存消耗将更加合理。

  1. You might want to check out Stack Overflow question .NET EXE memory footprint.
  2. MSDN 的博客文章 工作集! = 实际内存占用讲述了如何揭开工作集、进程内存的神秘面纱,以及如何对内存总消耗执行精确的计算。

I will not say that you should ignore the memory footprint of your application -- obviously, smaller and more efficient does tend to be desirable. However, you should consider what your actual needs are.

If you are writing a standard Windows Forms and WPF client applications which is destined to run on an individual's PC, and is likely to be the primary application in which the user operates, you can get away with being more lackadaisical about memory allocation. (So long as it all gets deallocated.)

然而,这里有些人说不用担心: 如果你正在编写一个 Windows 窗体应用程序,它将运行在一个终端服务环境中,在一个可能被10个、20个或更多用户使用的共享服务器上,那么是的,你绝对必须考虑内存使用情况。你要保持警惕。解决这个问题的最佳方法是进行良好的数据结构设计,并遵循有关分配时间和分配内容的最佳实践。

处理标题中的一般性问题,而不是处理 specific question:

如果您使用的 COM 组件返回大量数据 (比如大的2xN 双精度数组) ,只有一小部分 然后可以编写一个包装器 COM 组件 对.NET 隐藏内存,并只返回 是必须的。

这就是我在主应用程序中所做的 显著提高了记忆消耗。

有很多方法可以减少你的足迹。

.NET中必须始终处理的一个问题是,IL 代码的本机映像的大小是 巨大

此代码不能在应用程序实例之间完全共享。即使 NGEN‘ ed程序集也不是完全静态的,它们仍然有一些需要 JITting 的小部件。

人们还倾向于编写阻塞内存时间远远超过必要时间的代码。

一个常见的例子: 以 Datareader 为例,将内容加载到 DataTable 中,只是为了将其写入 XML 文件。您可以很容易地运行到 OutOfMemory 异常。OTOH,您可以使用 XmlTextWriter 并在 Datareader 中滚动,在滚动数据库光标时发出 XmlNodes。 这样,在内存中就只有当前数据库记录及其 XML 输出。它永远不会(或不太可能)获得更高的垃圾收集生成,因此可以重用。

The same applies to getting a list of some instances, doing some stuff (that spawns of thousands of new instances, which might stay referenced somewhere), and even though you don't need them afterwards, you still reference everything until after the foreach. 显式地使输入列表和临时副产品为空意味着,即使在退出循环之前,也可以重用此内存。

C # 有一个很棒的特性叫做迭代器。它们允许您通过滚动输入来对对象进行流处理,并且只保留当前实例,直到获得下一个实例。即使使用 LINQ,您仍然不需要保留所有内容,只是因为您希望对它进行过滤。

还有一些方法可以减少这个简单程序的私有工作集:

  1. NGEN 您的应用程序。这将从您的进程中消除 JIT 编译成本。

  2. Train your application using MPGO 减少内存使用 and then NGEN it.

我发现,使用 SetProcessWorkingSetSize 或 EmptyWorkingSet API 在长时间运行的进程中周期性地强制内存页面到磁盘,可以导致机器上所有可用的物理内存有效地消失,直到机器重新启动。我们有一个。NET DLL 加载到本机进程中,该进程将使用 EmptyWorkingSet API (替代使用 SetProcessWorkingSetSize)在执行内存密集型任务后减少工作集。我发现,在1天到1周之间的任何时候,一台计算机将在 Task Manager 中显示99% 的物理内存使用情况,而没有任何进程显示正在使用任何重要的内存使用情况。不久之后,机器会变得没有反应,需要硬重新启动。这些机器有超过24台 Windows Server 2008 R2和2012年的 r2服务器,它们都运行在物理和虚拟硬件上。

Perhaps having the .NET code loaded into a native process had something to do with it, but use EmptyWorkingSet (or SetProcessWorkingSetSize) at your own risk. Perhaps only use it once after initial launch of your application. I have decided to disable the code and let the Garbage Collector manage memory usage on its own.