6

我正在做一些Project Euler练习,我遇到了一个场景,我想要int大于 2,147,483,647( C# 中的上限)的数组。

当然这些是大数组,但例如,我不能这样做

// fails
bool[] BigArray = new BigArray[2147483648];

// also fails, cannot convert uint to int
ArrayList BigArrayList = new ArrayList(2147483648); 

那么,我可以有更大的数组吗?

编辑:这是为了阿特金筛子,你知道,所以我只想要一个非常大的:D

4

6 回答 6

12

每当您使用这么大的阵列时,您可能应该尝试找到更好的解决方案。但话虽如此,我仍然会尝试回答你的问题。

本文所述,.Net 中的任何对象都有 2 GB 的限制。适用于所有 x86、x64 和 IA64。

与 32 位 Windows 操作系统一样,在 64 位 Windows 操作系统上运行 64 位托管应用程序时可以创建的对象大小限制为 2GB。

此外,如果您在堆栈上定义的数组太大,您将有堆栈溢出。如果您在堆上定义数组,它将尝试将其全部分配在一个大的连续块中。最好使用在堆上具有隐式动态分配的 ArrayList。这不会让你超过 2GB,但可能会让你更接近它。

我认为只有在使用 x64 或 IA64 架构和操作系统时,堆栈大小限制才会更大。使用 x64 或 IA64,您将拥有 64 位可分配内存,而不是 32 位。

如果您不能一次全部分配数组列表,您可以分部分分配它。

在具有 6GB RAM 的 x64 Windows 2008 机器上使用数组列表并一次添加 1 个对象,我最多可以得到 ArrayList 的大小:134217728。所以我真的认为你必须找到一个更好的解决方案来解决你的问题不使用那么多内存。也许写入文件而不是使用 RAM。

于 2009-02-21T20:33:01.487 回答
8

数组限制是,afaik,即使在 64 位上也固定为 int32。单个对象的最大尺寸有上限。但是,您可以很容易地拥有一个漂亮的大锯齿状阵列。

更差; 因为 x64 中的引用更大,所以对于 ref 类型的数组,您实际上在单个数组中获得的元素更少。

这里

我收到了许多关于为什么 2.0 .Net 运行时的 64 位版本仍然将数组最大大小限制为 2GB 的问题。鉴于这似乎是最近的热门话题,我想出了一些背景知识,并讨论了绕过这个限制的选项。

首先是一些背景;在 .Net 运行时 (CLR) 的 2.0 版本中,我们做出了一个有意识的设计决定,将 GC 堆中允许的最大对象大小保持在 2GB,即使在运行时的 64 位版本上也是如此。这与 32 位 CLR 的当前 1.1 实现相同,但是您将很难在 32 位 CLR 上实际分配 2GB 对象,因为虚拟地址空间太碎片化而无法实际找到 2GB洞。通常人们并不特别关心创建实例化(或任何接近)时大于 2GB 的类型,但是由于数组只是在托管堆中创建的一种特殊类型的托管类型,因此它们也受到此限制的影响。


应该注意的是,在 .NET 4.5 中, gcAllowVeryLargeObjects标志可以选择性地删除内存大小限制,但是,这不会更改最大维度大小。关键是,如果您有自定义类型的数组或多维数组,那么您现在可以超过 2GB 的内存大小。

于 2009-02-21T20:38:16.017 回答
6

您根本不需要那么大的数组。

当你的方法遇到资源问题时,不要只看如何扩展资源,还要看方法。:)

这是一个使用 3 MB 缓冲区来使用 Eratosthenes 筛计算素数的类。该类跟踪您计算质数的距离,以及何时需要扩展范围,它会创建一个缓冲区来测试另外 300 万个数字。

它将找到的素数保存在一个列表中,当范围扩大时,previos 素数用于排除缓冲区中的数字。

我做了一些测试,大约 3 MB 的缓冲区是最有效的。

public class Primes {

   private const int _blockSize = 3000000;

   private List<long> _primes;
   private long _next;

   public Primes() {
      _primes = new List<long>() { 2, 3, 5, 7, 11, 13, 17, 19 };
      _next = 23;
   }

   private void Expand() {
      bool[] sieve = new bool[_blockSize];
      foreach (long prime in _primes) {
         for (long i = ((_next + prime - 1L) / prime) * prime - _next;
            i < _blockSize; i += prime) {
            sieve[i] = true;
         }
      }
      for (int i = 0; i < _blockSize; i++) {
         if (!sieve[i]) {
            _primes.Add(_next);
            for (long j = i + _next; j < _blockSize; j += _next) {
               sieve[j] = true;
            }
         }
         _next++;
      }
   }

   public long this[int index] {
      get {
         if (index < 0) throw new IndexOutOfRangeException();
         while (index >= _primes.Count) {
            Expand();
         }
         return _primes[index];
      }
   }

   public bool IsPrime(long number) {
      while (_primes[_primes.Count - 1] < number) {
         Expand();
      }
      return _primes.BinarySearch(number) >= 0;
   }

}
于 2009-02-21T21:21:17.613 回答
3

我相信即使在 64 位 CLR 中,每个对象也有 2GB(或者可能是 1GB——我记不清了)的限制。这将阻止您创建更大的数组。Array.CreateInstance 仅采用 Int32 参数作为大小的事实也具有启发性。

从更广泛的角度来看,我怀疑如果您需要这么大的数组,您应该真正改变解决问题的方式。

于 2009-02-21T20:38:46.403 回答
1

我是 C# 的新手(即本周学习它),所以我不确定如何实现 ArrayList 的确切细节。但是,我猜由于您没有为 ArrayList 示例定义类型,因此该数组将被分配为对象引用数组。这很可能意味着您实际上分配了 4-8Gb 的内存,具体取决于架构。

于 2009-02-22T00:25:07.623 回答
0

根据 MSDN,字节数组的索引不能大于 2147483591。对于 .NET 4.5 之前的版本,它也是数组的内存限制。在 .NET 4.5 中,这个最大值是相同的,但对于其他类型,它可以达到 2146435071。

这是用于说明的代码:

    static void Main(string[] args)
    {
        // -----------------------------------------------
        // Pre .NET 4.5 or gcAllowVeryLargeObjects unset
        const int twoGig = 2147483591; // magic number from .NET

        var type = typeof(int);          // type to use
        var size = Marshal.SizeOf(type); // type size
        var num = twoGig / size;         // max element count

        var arr20 = Array.CreateInstance(type, num);
        var arr21 = new byte[num];

        // -----------------------------------------------
        // .NET 4.5 with x64 and gcAllowVeryLargeObjects set
        var arr451 = new byte[2147483591];
        var arr452 = Array.CreateInstance(typeof(int), 2146435071);
        var arr453 = new byte[2146435071]; // another magic number

        return;
    }
于 2014-09-15T20:47:26.120 回答