简单的C#数组抛出了OutOfMemory异常

5

我有一个相当大而又相对简单的数组,跟踪着750,000个条目。在64位.NET中它可以正常工作,但是在32位客户端中会出现OutOfMemory异常。我知道我并没有耗尽内存。需要运行此程序的客户端无法使用64位版本。

以下伪代码编译通过(编辑:原始代码由于拼写错误而未编译通过,已经修复),在两台机器上针对x86架构时都会抛出相同的异常:

  • 一台安装了4 GB RAM和.NET Framework 4的Windows XP 32位机器
  • 一台安装了8 GB RAM和.NET Framework 4的Windows 7 64位机器

编辑:将编译器标志切换为显式地针对x64架构,则可以在Windows 7 64位机器上运行。

using System;

public class Storage
{
    Storage futureStorage;
    int count;
    int years;
    bool isTest;
    bool isSet;
}

public static class Program
{
    public static void Main()
    {
        Storage[] storageArray = new Storage[750000];
        for (int i = 0; i < 750000; i++)
        {
            storageArray[i] = new Storage();
        }
    }
}


编辑:

这段代码运行的网络是封闭网络,没有外部网络连接,因此发布更精确的代码和相关异常细节需要一些过程,我正在努力解决。我已经运行了intellitrace,具有完整的调试符号和一些编译器开关,更多细节将在我将其移植到互联网连接的网络后提供。


在两台机器上,代码在索引为693,000(确切地说,在XP上为693,002,在Windows 7上为693,646)时停止工作。

有人有什么想法,可能是什么问题?当我在代码后进行转储和分析时,我几乎没有使用25 MB的RAM。

如果使用不安全/非托管代码可以帮助我解决问题,我会切换,但我尝试过的唯一尝试使我无法使用我的Storage类。

更新:

似乎有几个人无法复制该问题(包括我在另一台机器/网络上)。

然而,我想出了一个解决方案(使用LINQ)来缓解这个问题,现在它只有1/5的概率抛出异常,有人知道使用此解决方案是否存在任何问题或注意事项,以及为什么内部可能会与其他解决方案不同?

using System;
using System.Linq;    

public class Storage
{
    Storage futureStorage;
    int count;
    int years;
    bool isTest;
    bool isSet;
}

public static class Program
{
    public IEnumerable<Storage> storageEnumerable;
    public static void Main()
    {
        storageEnumerable = (from x in  Enumerable.Range(0, 750000) select new Storage() {count = x});
        foreach (Storage s in storageEnumerable)
            Console.WriteLine(x);
    }
}

上述LINQ代码使用的内存要少得多,但异常仍然发生;而且只发生在这个网络中的机器上,而不是在网络之外的其他机器上(或者说在IIS应用程序池内)。
考虑到其他用户后来报告说不能重现这个bug,可能是由于环境或硬件特定引起。是否有网络监视工具或反病毒程序触发了这两个代码实现?如果是这样,有没有更好/推荐的方法来初始化数组,以避免这种情况?

2
如果你使用short count, years;,你可以将存储几乎减少一半。 - Peter Gluck
4个回答

1

尝试将您的存储结构体 struct 改为 class。 值类型数组比引用类型数组占用更少的内存。


由于引用了自身类型,因此无法实现。 - leppie

1

那篇文章没有解决问题,但它确实将频率从1/5降低到了大约1/1700左右;我找到了一种方法来捕获并重试它,虽然不是最有效的资源利用方式,但它能够工作,并且通常是可以接受的。 - Brian Deragon

1

根据您提供的更多调试信息,我的猜测是您的程序使用了虚拟内存。请使用任务管理器探索所使用的所有内存,您需要将这些字段添加到进程资源管理器中。如果是这种情况,您的确会耗尽内存。


1

就记录而言,来自 Jeffrey Richter(线程工作章节)的内容如下:

32 位进程在其内部最多具有 2 GB 的可用地址空间。在加载一堆 Win32 DLL、CLR DLL、本机堆和托管堆后,大约还剩下 1.5 GB 的地址空间。

尝试创建更多线程将导致抛出 OutOfMemoryException 异常。

当然,64 位进程提供了 8 TB 的地址空间,因此理论上可以创建数十万个线程。

因此,在初始化数组之后,可用空间越来越少。并且在某些时候,您将没有空闲内存,并且会收到 OutOfMemoryException

我认为 LINQ 使用资源更加明智,在某些情况下可以无错误运行。

不幸的是,您不能对此做太多事情,很抱歉。唯一能做的事情:

  • 在数组中不使用太多对象
  • 经常使用 GC.Collect 来回收不再使用的对象

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