CLR内存消耗问题

5
CLR存在的一个问题是在缺乏RAM时表现极差(当托管进程的某些内存被分页出去时,这会导致整个系统完全冻结,甚至无法访问Ctrl-Alt-Del屏幕。我认为原因是GC试图构建可达对象的图形并尝试扫描进程的所有内存,导致大量的页面进/页面出操作)。
这使得我的.NET程序存在问题,因为当输入数据很大时,它可能会消耗大量的RAM。

我更喜欢向用户显示“内存不足”消息,而不是完全挂起他的系统^_^
有没有办法实现这一点?


3
你的问题根本原因可能是计算机物理内存太少,无法有效地执行所需的工作负载。最好的解决方案可能是增加物理内存或减少工作负载。 - Martin Liversage
2
同意。还有一个严重碎片化的分页文件。 - Hans Passant
1
@Martin,我的电脑没问题。问题在于输入数据可以是任意大小,因此任何数量的RAM都可能太小了。 - fithu
3
也许你需要重写你的应用程序,避免将所有输入数据读入内存。或者,如果你想阻止用户读取过多的数据,请在读取数据之前给出错误信息,而不是试图想出一个可以通知用户已经读取太多数据的黑客方法。 - Martin Liversage
@fithu,不要被他们打倒。这里存在一个盲点,即.Net的支持者并不真正相信在人工限制.Net进程使用的内存与JVM的最大堆大小参数之间存在充分理由。说你应该重写你的应用程序以避免消耗过多的内存就像去看医生,“当我做这个时很疼”,他的回答是“不要做那个”。无论它有多真实,它同样没有帮助。 - Kelly S. French
显示剩余4条评论
4个回答

3
使用 MemoryFailPoint,您可以告诉.NET您需要一定数量的内存。但问题在于,即使这个系统包括交换空间。
我认为在这里实现您想要实现的目标非常困难。假设您使用某些系统指标和性能指标来查找可用的物理内存量,并基于此执行某些任务。如果在您完成此检查之后有一个不同的进程进入并占用了物理内存,则您最初的计算将不再适用,并且您的某些内存将被推送到交换空间中。
我有一个建议。您可以设置一个配置选项,用于应用程序使用的最大允许内存量。通过这样做,您可以:
  1. 尝试根据应用程序消耗的资源(例如网络连接,如果您的应用程序是网络服务器)来确定其所需的资源量,并根据最大内存消耗来限制连接数量;

  2. 您可以运行第二个线程,每隔10秒或1分钟使用GC.GetTotalMemory()检查总内存消耗,并在达到最大值时开始拒绝连接(同样适用于网络服务器)。

这应该是一个配置设置,而不是可用物理内存的数量,因为您不知道机器上运行了哪些其他应用程序。


那很糟糕。我可以想象这种情况发生。我有一个建议。是否可以设置一个配置选项,以限制应用程序使用的最大内存量?我不知道这是否可行,但也许您可以通过控制资源消耗来大致保持在这些限制内。您甚至可以启动一个辅助线程,每隔10秒或一分钟使用GC.GetTotalMemory()检查总消耗,并一旦超过限制就开始拒绝连接(如果您是网络服务器)。 - Pieter van Ginkel

2
有一些系统调用可以获取进程大小和机器上的RAM账户,这可能会有所帮助。
以前有很多关于编写能够处理分页的GC的研究,但我认为它们永远不会发布,因为现在的RAM变得越来越大。(基本的想法是不收集任何被分页的对象,并尝试在操作系统将其分页之前收集所有页面上的对象。但你需要知道所有可能被分页对象指向的对象。)
您可以使用结构体数组,然后传递索引来减少您拥有的对象数量,从而减少GC必须跟随的指针数量。只有当您需要大量相同类型的数据时,这才值得做。

他们至少可以让CLR显示错误消息,而不是在操作系统尝试将一些托管内存分页时冻结整个系统。 - fithu
@fithu,不是那么简单的,任何垃圾回收器都可以应对某种程度的页面处理,这取决于对象聚集的程度。例如,指向彼此的对象是否大多在同一时间创建。 - Ian Ringrose
是的,RAM变得更大了... 是的,任务并不简单... 但是,无论如何... 我想,在任何情况下,任何程序都不能冻结整个系统。这显然是个错误。 - fithu

1
在Windows中,您可以使用作业对象来对进程可以分配的虚拟内存数量设置硬限制。当CLR达到此限制时,将自动进行垃圾回收,并在无法释放足够空间时抛出OutOfMemoryException。您还可以限制进程的工作集而不是虚拟内存。这使进程可以分配所需的内存,但会被交换出而不是消耗RAM并导致系统挂起。我已经成功地使用作业对象实现了这个目的(在运行大型作业时保持机器响应)。
要从.NET创建作业对象,必须使用PInvoke调用Win32 API。这CodeProject上的文章解释了该过程。

0

让IIS托管进程并使用IIS中的内存控件来限制所使用的内存量。希望这将更频繁地强制进行GC并最小化需要长时间运行的GC会话的需求。我想这种行为是由于您的应用程序从生成方面使用内存的方式,即大量小对象是1)长期存在且2)在内存中移动。

其他类似的.NET/CLR内存控制问题和解决方案的链接

配置 .NET CLR 的 RAM 使用量
限制 .Net CLR 的内存使用量
虚拟和物理内存 / OutOfMemoryException
使用 GC.AddMemoryPressure() 预防 OutOfMemoryException?
强制回收数组的垃圾,C#
垃圾回收瓶颈的示例
抑制 C# 垃圾回收
如何分析 .net 垃圾回收器?

如果您正在使用大对象堆(LOH)

.NET集合和大对象堆(LOH)
大对象堆碎片化


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接