C# 减少 .NET 应用程序的内存使用量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1343374/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reducing memory usage of .NET applications?
提问by Robert Fraser
What are some tips to reduce the memory usage of .NET applications? Consider the following simple C# program.
有哪些技巧可以减少 .NET 应用程序的内存使用量?考虑以下简单的 C# 程序。
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
}
}
Compiled in releasemode for x64and running outside Visual Studio, the task manager reports the following:
在x64 的发布模式下编译并在 Visual Studio 外运行,任务管理器报告以下内容:
Working Set: 9364k
Private Working Set: 2500k
Commit Size: 17480k
It's a little better if it's compiled just for x86:
如果只为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 x86Releaseoutside Visual Studio:
在 Visual Studio 之外的x86 Release上的结果:
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 Domainwhich means the user interface stuff can be safely unloaded, but taking up 10 MB when it's just sitting in the background seems excessive.
哪个好一点,但对于这样一个简单的程序来说仍然显得过分。有什么技巧可以让 C# 过程更精简一些吗?我正在编写一个大部分时间都在后台运行的程序。我已经在一个单独的应用程序域中做任何用户界面的东西,这意味着用户界面的东西可以安全地卸载,但是当它只是坐在后台时占用 10 MB 似乎过多。
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.
PS至于我为什么会关心---(超级)用户往往会担心这些事情。即使它对性能几乎没有影响,半精通技术的用户(我的目标受众)往往会对后台应用程序内存使用情况感到不满。甚至当我看到 Adobe Updater 占用 11 MB 内存时,我也感到害怕,而 Foobar2000 的平静触感让我感到安慰,即使在播放时它也可以占用不到 6 MB。我知道在现代操作系统中,这些东西在技术上真的没有那么重要,但这并不意味着它不会影响感知。
采纳答案by John Rudy
- You might want to check out Stack Overflow question .NET EXE memory footprint.
- The MSDN blog post Working set != actual memory footprintis all about demystifying the working set, process memory and how to perform accurate calculations on your total in-RAM consumption.
- 您可能想查看堆栈溢出问题.NET EXE 内存占用。
- 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 窗体和 WPF 客户端应用程序,它注定要在个人 PC 上运行,并且可能是用户操作的主要应用程序,那么您可以避免对内存分配更加松懈。(只要这一切都被解除分配。)
However, to address some folks here who say not to worry about it: If you're writing a Windows Forms application which will be running in a terminal services environment, on a shared server possibly utilized by 10, 20 or more users, then yes, you absolutely must consider memory usage. And you will need to be vigilant. The best way to address this is with good data structure design and by following best practices regarding when and what you allocate.
但是,为了解决一些在这里说不用担心的人:如果您正在编写将在终端服务环境中运行的 Windows 窗体应用程序,在可能由 10、20 或更多用户使用的共享服务器上,那么是的,您绝对必须考虑内存使用情况。你需要保持警惕。解决此问题的最佳方法是采用良好的数据结构设计,并遵循有关分配时间和分配内容的最佳实践。
回答by Donut
No specific suggestions per se, but you might take a look at the CLR Profiler(free download from Microsoft).
Once you've installed it, take a look at this how-to page.
本身没有具体建议,但您可以查看CLR Profiler(从 Microsoft 免费下载)。
安装完成后,请查看此操作方法页面。
From the how-to:
从操作方法:
This How To shows you how to use the CLR Profiler tool to investigate your application's memory allocation profile. You can use CLR Profiler to identify code that causes memory problems, such as memory leaks and excessive or inefficient garbage collection.
此操作方法向您展示如何使用 CLR Profiler 工具调查应用程序的内存分配配置文件。您可以使用 CLR Profiler 来识别导致内存问题的代码,例如内存泄漏和过度或低效的垃圾回收。
回答by Brian Rasmussen
.NET applications will have a bigger footprint compared to native applications due to the fact that they both have to load the runtime and the application in the process. If you want something really tidy, .NET may not be the best option.
与本机应用程序相比,.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.
但是,请记住,如果您的应用程序大部分时间处于休眠状态,则必要的内存页面将被交换出内存,因此在大多数情况下对系统来说实际上并没有那么大的负担。
If you want to keep the footprint small, you will have to think about memory usage. Here are a couple of ideas:
如果您想保持较小的占用空间,则必须考虑内存使用情况。这里有几个想法:
- Reduce the number of objects and make sure not to hold on to any instance longer than required.
- Be aware of
List<T>
and similar types that double capacity when needed as they may lead to up 50% waste. - You could consider using value types over reference types to force more memory on the stack, but keep in mind that the default stack space is just 1 MB.
- Avoid objects of more than 85000 bytes, as they will go to LOH which is not compacted and thus may easily get fragmented.
- 减少对象的数量并确保不要持有任何超过所需时间的实例。
- 请注意
List<T>
在需要时将容量加倍的类似类型,因为它们可能会导致 50% 的浪费。 - 您可以考虑使用值类型而不是引用类型来强制在堆栈上使用更多内存,但请记住,默认堆栈空间仅为 1 MB。
- 避免超过 85000 字节的对象,因为它们将进入未压缩的 LOH,因此很容易被碎片化。
That is probably not an exhaustive list by any means, but just a couple of ideas.
无论如何,这可能不是一个详尽的清单,而只是一些想法。
回答by JaredPar
One thing you need to consider in this case is the memory cost of the CLR. The CLR is loaded for every .Net process and hence factors into the memory considerations. For such a simple / small program the cost of the CLR is going to dominate your memory footprint.
在这种情况下,您需要考虑的一件事是 CLR 的内存成本。为每个 .Net 进程加载 CLR,因此将其纳入内存考虑因素。对于这样一个简单/小程序,CLR 的成本将支配您的内存占用。
It would be much more instructive to construct a real application and view the cost of that compared to the cost of this baseline program.
与此基准程序的成本相比,构建一个真实的应用程序并查看其成本会更有指导意义。
回答by Eric Petroelje
Might want to look at the memory usage of a "real" application.
可能想查看“真实”应用程序的内存使用情况。
Similar to Java there is some fixed amount of overhead for the runtime regardless of the program size, but memory consumption will be much more reasonable after that point.
与 Java 类似,无论程序大小如何,运行时都有一些固定的开销,但在那之后内存消耗将更加合理。
回答by Peter Mortensen
Addressing the general question in the title and not the specific question:
解决标题中的一般问题而不是具体问题:
If you are using a COM component that returns a lot of data (say large 2xN arrays of doubles) and only a small fraction is needed then one can write a wrapper COM component that hides the memory from .NET and returning only the data that is needed.
如果您使用的 COM 组件返回大量数据(比如大的 2xN 双精度数组)并且只需要一小部分,那么您可以编写一个包装 COM 组件,该组件对 .NET 隐藏内存并仅返回数据需要。
That is what I did in my main application and it significantly improved the memory consumption.
这就是我在主应用程序中所做的,它显着改善了内存消耗。
回答by Robert Giesecke
There are many ways to reduce your footprint.
有很多方法可以减少您的足迹。
One thing you'll always have to live with in .NETis that the size of the native image of your IL code is huge
在.NET 中您必须始终忍受的一件事是您的 IL 代码的本机映像的大小是巨大的
And this code cannot be completely shared between application instances. Even NGEN'edassemblies are not completely static, they have still some little parts that need JITting.
并且此代码不能在应用程序实例之间完全共享。即使经过 NGEN 处理的程序集也不是完全静态的,它们仍然有一些需要 JITting 的小部件。
People also tend to write code that blocks memory far longer than necessary.
人们还倾向于编写比必要时间更长的内存阻塞代码。
An often seen example: Taking a Datareader, loading the contents into a DataTable just to write it into an XML file. You can easily run into an OutOfMemoryException. OTOH, you could use an XmlTextWriter and scroll through the Datareader, emitting XmlNodes as you scroll through the database cursor. That way, you only have the current database record and its XML output in memory. Which will never (or is unlikely to) get a higher garbage collection generation and thus can be reused.
一个常见的例子:以Datareader为例,将内容加载到DataTable中只是为了将其写入XML文件。您很容易遇到 OutOfMemoryException。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. Explicitly null-ing your input list and your temporary by-products means, this memory can be reused even before you exit your loop.
这同样适用于获取一些实例的列表,做一些事情(产生数千个新实例,它们可能会在某处被引用),即使之后你不需要它们,你仍然引用所有内容,直到 foreach 之后。显式清空输入列表和临时副产品意味着,即使在退出循环之前,也可以重用这些内存。
C# has an excellent feature called iterators. They allow you to stream objects by scrolling through your input and only keep the current instance until you get the next one. Even by using LINQ, you still don't need to keep all of it around just because you wanted it to be filtered.
C# 有一个很好的特性叫做迭代器。它们允许您通过滚动输入来流式传输对象,并且只保留当前实例,直到获得下一个实例。即使使用 LINQ,您仍然不需要仅仅因为您希望对其进行过滤而保留所有内容。
回答by Feng Yuan
There are still ways to reduce the private working set of this simple program:
还有一些方法可以减少这个简单程序的私有工作集:
NGEN your application. This removes JIT compilation cost from your process.
Train your application using MPGO reducing memory usageand then NGEN it.
回答by Stuart Welch
I have found that using the SetProcessWorkingSetSize or EmptyWorkingSet APIs to force memory pages to disk periodically in a long running process can result in all available physical memory on a machine to effectively disappear until the machine is rebooted. We had a .NET DLL loaded into a native process which would use the EmptyWorkingSet API (an alternative to using SetProcessWorkingSetSize) to reduce the working set after performing a memory intensive task. I found that after anywhere between 1 day to a week a machine would show 99% physical memory usage in Task Manager while no processes were shown to be using any significant memory usage. Soon after the machine would become unresponsive, requiring a hard reboot. Said machines were over 2 dozen Windows Server 2008 R2 and 2012 R2 servers running on both physical and virtual hardware.
我发现在长时间运行的进程中使用 SetProcessWorkingSetSize 或 EmptyWorkingSet API 强制内存页面定期到磁盘会导致机器上所有可用的物理内存有效消失,直到机器重新启动。我们将 .NET DLL 加载到本机进程中,该进程将使用 EmptyWorkingSet API(使用 SetProcessWorkingSetSize 的替代方法)在执行内存密集型任务后减少工作集。我发现在 1 天到一周之间的任何时间后,机器将在任务管理器中显示 99% 的物理内存使用率,而没有显示任何进程正在使用任何显着的内存使用率。不久之后机器将变得无响应,需要硬重启。所述机器是在物理和虚拟硬件上运行的超过 2 打 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.
也许将 .NET 代码加载到本机进程中与它有关,但使用 EmptyWorkingSet(或 SetProcessWorkingSetSize)风险自负。也许只在您的应用程序初始启动后使用它一次。我决定禁用代码并让垃圾收集器自行管理内存使用。