我已经多次开发了具有非常大的分支或链接结构的小对象(每个<1kb)的应用程序,1)简单地创建对象或2)创建和访问它们。
在这两种情况下,一旦物理上可用的RAM耗尽,应用程序要么完全停止,要么直接抛出OutOfMemory。
我的理解是,一旦物理RAM耗尽,就应该进行分页,并且虽然速度非常慢,但程序仍应继续工作。特别是我既没有尝试分配大型对象,也没有使用超过2g对象的数组(或列表)(我不确定这个限制是否仍然适用于现在)。
我写了一个小测试程序,它不断分配和存储1GB内存块。由于.NET似乎延迟了分配,我还用数据填充了块。一旦RAM用完,我发现.NET正确地分页到磁盘,程序变得明显变慢,但从未崩溃或OOM-ed。
那么,为什么.NET似乎在很多小对象上遇到麻烦呢?这个特殊于我的配置吗?
答案 0 :(得分:3)
分页是操作系统的一项功能,因此这不是.NET所做或不做的事情。
OOM的常见来源是内存碎片。请记住,您实际上并未在托管应用程序中分配任何内容。你只需创建对象。运行时分配存储这些对象所需的内存。这些分配是在称为段的块中完成的。这些被分配为连续的内存。碎片可能导致没有足够的连续内存来支持新段的分配。如果无法分配段,则运行时将抛出OOM。
OOM的另一个常见来源是地址空间耗尽。一个常见的误解是,只要系统上有足够的可用内存,就不应该发生OOM。 32位应用程序的情况并非如此。它们的地址空间为2GB(如果是64位,则为4GB,可识别大内存地址)。无论系统可能有多少内存,任何高于此值都会触发OOM。由于您的问题被标记为x64,我假设这是一个64位应用程序,在这种情况下,地址空间耗尽可能不是这种情况。
就目前而言,您的问题确实包含足够的信息来说明为什么您会看到OOM异常。您是否将所有这些小对象存储在列表或其他结构中?如果是这样,您可能正在使用非常大的数组,因为许多集合类是使用数组实现的。
答案 1 :(得分:1)
Windows正确处理.NET的分页以及任何其他进程。从某种意义上说,分页不应该是一个问题,因为Windows决定哪些页面保留在物理内存中而不是那些被分页到磁盘上的页面。耗尽物理内存并不是一个好兆头,因为这意味着系统中的所有进程都需要频繁访问不适合物理内存的页面。
OOM因其他原因而发生:
x86进程在32位计算机或WOW64下的64位计算机上耗尽进程地址空间。 基本上这样的过程默认不能超过2GB。 x86进程可能使用/ 3GB选项在32位机器上使用3GB,或者如果它们被标记为可识别大地址,则可以在WOW64下使用。
要查看它是否以2GB,3GB或4GB的亮度进入墙壁,请使用性能监视器下的虚拟大小计数器或从Microsoft下载 Process Explorer 。我已经测试了几次,它总是会崩溃大约1,980,000KB的虚拟大小。请注意,任务管理器在Windows 7上只有提交大小,并且不如虚拟大小准确。
内存碎片也可能是一个原因,但是当你只有被垃圾收集器压缩的小对象时,我认为它不适用。
耗尽资源 如果您的小对象使用未管理的GDI句柄,默认情况下您可能会耗尽10000左右。在这种情况下,您将在屏幕的一个或多个部分上获得伴随红色X(交叉)的OOM错误。
提交大小包含已分配和正在使用的所有页面(无论此时是否已分页或在RAM中)。它代表了进程当前对内存的需求。虚拟大小是完整的进程地址空间,包括已提交和保留的段。后者是保留供将来使用的占位符地址,但尚未使用或分配(甚至不在页面文件中)。
答案 2 :(得分:0)
I wrote a little program确认,作为Brian Rasmussen's pointed out,.NET能够很好地分页到磁盘。
琐碎程序,它不断分配RAM块以测试系统行为。
分配将以1GB大小开始的块进行。的数量 块是无限的。如果抛出OutOfMemory异常,则 尺寸将减少。每个块都将填充数据以确保其 内存页面实际上已创建并初始化。
特别是,在x64盒子上运行,你应该能够观察到 填写可用后,.NET可以很好地分页到磁盘 物理RAM。
请注意,这很可能会严重降低您的系统速度, 可能会在进程中崩溃其他正在运行的应用程序(Web 浏览器因此而臭名昭着。)