【问题标题】:Does .NET correctly handle memory paging with many small objects?.NET 是否正确处理带有许多小对象的内存分页?
【发布时间】:2014-12-13 12:35:21
【问题描述】:

我曾多次开发过具有非常大的小对象(每个小于 1kb)的分支或链接结构的应用程序,要么 1)简单地创建对象,要么 2)创建和访问它们。

在这两种情况下,一旦我们用完物理可用的 RAM,应用程序要么完全停止,要么直接抛出 OutOfMemory。

我的理解是,一旦物理 RAM 用完,就会发生分页,虽然速度很慢,但程序应该继续工作。特别是我既没有尝试分配大对象,也没有使用超过 2g 对象的数组(或列表)(我不确定这个限制现在是否仍然适用)。

我写了一个小测试程序,它连续分配和存储 1GB 的内存块。由于 .NET 似乎延迟了分配,所以我也用数据填充了这些块。我发现一旦 RAM 用完,.NET 会正确地分页到磁盘,并且程序变得明显变慢,但从未崩溃或 OOM-ed。

那么,为什么 .NET 似乎在处理大量小对象时会遇到问题?这是我的配置所特有的吗?

【问题讨论】:

  • 您确定您的对象总数不超过 2GB?限制不是“一个列表可以有 2GB 的对象”,而是"A 32 bit process can only have 2 to 3GB of virtual address space allocated to it total"。那将是 2,000,000 个 1kb 对象的硬性限制。尽管由于进程中所有其余对象的内存开销,您更有可能远低于该数字。在您的程序上运行分析器以查看您正在使用多少对象和多少内存。
  • 将其设为 64 位进程并查看进程的私有字节计数器。还要查看系统提交费用(100% 分配失败)和物理使用情况(大致表明分页开始发生的时间)。
  • 这是一个 64 位进程。
  • @usr 我会检查值

标签: .net memory out-of-memory


【解决方案1】:

分页是操作系统的一个特性,所以这不是 .NET 做或不做的事情。

OOM 的常见来源是内存碎片。请记住,您实际上并没有在托管应用程序中分配任何内容。您只需创建对象。运行时分配存储这些对象所需的内存。这些分配是在称为段的块中完成的。这些被分配为连续内存。碎片可能导致没有足够的连续内存来分配新段的情况。如果无法分配段,则运行时将引发 OOM。

OOM 的另一个常见来源是地址空间耗尽。一个常见的误解是,只要系统上有足够的可用内存,就不应该发生 OOM。 32 位应用程序并非如此。它们的地址空间为 2GB(如果在 64 位和大内存地址感知上,则为 4GB)。无论系统可能有多少内存,高于此值的任何内容都会触发 OOM。由于您的问题被标记为 x64,我假设这是一个 64 位应用程序,在这种情况下,地址空间耗尽可能不是这种情况。

就目前而言,您的问题确实包含足够的信息来说明您看到 OOM 异常的原因。您是否将所有这些小对象存储在列表或其他结构中?如果是这样,您可能正在使用非常大的数组,因为许多集合类都是使用数组实现的。

【讨论】:

  • 我没有说清楚,但是进程是64位的。我的印象是内存碎片在 64 位应用程序中是无关紧要的,并且不会发生耗尽(至少在几十年内)。
  • 对象存储在一个非常类似于一棵大树的结构中。单个节点本身永远不会超过几千个连接。
  • 除了OOM之外,整个应用程序也倾向于无限期冻结(我在一小时后将其杀死)。
  • 延迟很可能是GC超时造成的。我不能说为什么你会看到 OOM,但你可以使用 WinDbg/SOS 来调查托管对象和分配的段。也许这可以给你一些线索。
  • 我不知道 SOS,感谢您的提示。供参考:msdn.microsoft.com/en-us/library/bb190764(v=vs.110).aspx
【解决方案2】:

Windows 正确处理 .NET 的分页以及与此相关的任何其他进程。从某种意义上说,分页不应该成为真正的问题,因为 Windows 的工作是决定哪些页面保留在物理内存中,哪些页面被分页到磁盘。用完物理内存并不是一个好兆头,因为这意味着系统中的所有进程都需要频繁访问实际上不适合物理内存的页面。

OOM 的发生有其他原因:

  • 一个 x86 进程在 32 位机器上或在 WOW64 下的 64 位机器上的进程地址空间不足。 基本上这样的进程默认不能超过2GB。如果 x86 进程被标记为大地址感知,则 x86 进程可能能够在 32 位机器上使用 /3GB 选项或在 WOW64 下使用 3GB。

    要了解它是否在 2GB、3GB 或 4GB 时遇到问题,请使用性能监视器下的虚拟大小计数器或从 Microsoft 下载 Process Explorer。我已经对此进行了几次测试,它总是在大约 1,980,000KB 的虚拟大小时崩溃。请注意,任务管理器仅在 Windows 7 上具有提交大小,并且不如虚拟大小准确。

  • 内存碎片也可能是一个原因,但我认为它不适用于只有小对象被垃圾收集器压缩的情况。

  • 资源不足 如果您的小对象使用不受管理的 GDI 句柄,默认情况下您可能会用完大约 10000 个。在这种情况下,您将在屏幕的一个或多个部分出现伴随红色 X(叉号)的 OOM 错误。

提交大小包含所有已分配和正在使用的页面(无论此时是否被调出或在 RAM 中)。它代表进程当前对内存的需求。虚拟大小是包括提交和保留段的完整进程地址空间。后者是为将来使用而保留的占位符地址,但尚未使用或分配(甚至在页面文件中也没有)。

【讨论】:

  • 你能扩展一下虚拟与提交大小吗?
  • Mafu,添加了信息提交大小与虚拟大小。我希望它有所帮助。
【解决方案3】:

I wrote a little program 确认,作为Brian Rasmussen's pointed out,.NET 能够很好地分页到磁盘。

一个简单的程序,它不断分配 RAM 块来测试系统行为。

分配将从 1GB 大小开始以块的形式进行。的数量 块是无限的。如果抛出 OutOfMemory 异常,则 尺寸会减小。每个块将填充数据以确保其 内存页实际上是被创建和初始化的。

特别是在 x64 机器上运行,您应该能够观察到 .NET 能够在填充可用后很好地分页到磁盘 物理内存。

请注意,这很可能会严重降低您的系统速度, 可能会导致进程中其他正在运行的应用程序崩溃(网络 浏览器因此而臭名昭著)。

【讨论】:

    猜你喜欢
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2016-08-06
    相关资源
    最近更新 更多