Ограничен ли размер массива верхним пределом int (2147483647)?

Я выполняю несколько упражнений Project Euler и столкнулся со сценарием, когда у меня есть нужны массивы, размер которых превышает 2147483647 (верхний предел int в C #).

Конечно, это большие массивы, но я, например, не могу этого сделать

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

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

Итак, могу ли я иметь массивы побольше?

РЕДАКТИРОВАТЬ: это было для Сита Аткина, вы знаете, поэтому я просто хотел действительно большой один D


person inspite    schedule 21.02.2009    source источник
comment
Если вы пытаетесь создать такой массив для решения проблемы Эйлера проекта, то я думаю, что вы выбрали плохую стратегию решения проблемы. (Не знаю, можно ли создавать большие массивы на x64, надеюсь, кто-то даст реальный ответ на ваш вопрос .Net.)   -  person Brian    schedule 21.02.2009
comment
Да, я знаю, что это так (re: стратегия), но я был просто шокирован, когда достиг предела!   -  person inspite    schedule 21.02.2009
comment
Я задавал тот же вопрос раньше, не могу получить исчерпывающий ответ, надеюсь, вы получите ответ, чтобы преодолеть эту проблему .. stackoverflow.com/questions/494923/   -  person Canavar    schedule 21.02.2009
comment
@ScarletGarden: спасибо. раздражает не правда ли: D   -  person inspite    schedule 21.02.2009
comment
для записи, я сделал несколько действительно небольших настроек, и решение пришло примерно за 6 секунд, это была проблема 187   -  person inspite    schedule 22.02.2009
comment
Ошибка uint to int связана с тем, что вы инициализируете int.MaxValue + 1, а не int.MaxValue.   -  person Zooba    schedule 22.02.2009
comment
Почему вы пытаетесь использовать ArrayList? System.Collections.Generic.List ‹T› намного лучше в этом контексте, особенно потому, что вам не понадобится бокс.   -  person Hosam Aly    schedule 10.03.2009
comment
@Hosam, да, спасибо, я это знаю сейчас: D   -  person inspite    schedule 10.03.2009


Ответы (6)


Каждый раз, когда вы работаете с массивом такого размера, вам, вероятно, следует попытаться найти лучшее решение проблемы. Но при этом я все же попытаюсь ответить на ваш вопрос.

Как упоминалось в этой статье, есть 2 Предел ГБ для любого объекта в .Net. Для всех x86, x64 и IA64.

Как и в 32-битных операционных системах Windows, существует ограничение в 2 ГБ на размер объекта, который вы можете создать при запуске 64-битного управляемого приложения в 64-битной операционной системе Windows.

Кроме того, если вы определите в стеке слишком большой массив, произойдет переполнение стека. Если вы определите массив в куче, он попытается разместить все в одном большом непрерывном блоке. Было бы лучше использовать ArrayList с неявным динамическим распределением в куче. Это не позволит вам преодолеть 2 ГБ, но, вероятно, позволит вам приблизиться к ним.

Я думаю, что ограничение на размер стека будет больше, только если вы используете архитектуру и операционную систему x64 или IA64. При использовании x64 или IA64 у вас будет 64-разрядная выделяемая память вместо 32-разрядной.

Если вы не можете выделить список массивов сразу, вы, вероятно, можете выделить его по частям.

Используя список массивов и добавляя по одному объекту за раз на машине x64 Windows 2008 с 6 ГБ ОЗУ, максимум, что я могу получить для ArrayList, - это размер: 134217728. Поэтому я действительно думаю, что вам нужно найти лучшее решение своей проблемы, которое не использует столько памяти. Возможно, запись в файл вместо использования ОЗУ.

person Brian R. Bondy    schedule 21.02.2009
comment
но я не могу этого сделать: ArrayList BigArrayList = new ArrayList (2147483648); либо - person inspite; 21.02.2009
comment
переполнение стека: я понимаю, что массив находится в стеке, если это локальная переменная, но вы говорите, что содержимое массива также выделяется в стеке (а не в куче)? - person ChrisW; 22.02.2009
comment
Я согласен. Это будет ограничение кучи, а не стека. - person recursive; 22.02.2009
comment
Я выделил 2,5 ГБ одним блоком на Linux x86. - person Joshua; 22.02.2009
comment
ChrisW, согласен, уточнил я. - person Brian R. Bondy; 22.02.2009
comment
Джошуа: Linux - это не Windows, и это не .NET. ;) Этот ответ говорит только о том, что 1) 32-разрядная Windows по умолчанию накладывает ограничение на 2 ГБ для 32-разрядных процессов и 2) Даже в 64-разрядной версии .NET накладывает ограничение на 2 ГБ для любого отдельного объекта. Linux не ограничивает 32-битные процессы 2 ГБ. - person jalf; 22.02.2009

Ограничение массива, afaik, фиксировано как int32 даже в 64-битной версии. Существует ограничение на максимальный размер одного объекта. Тем не менее, у вас довольно легко может быть хороший большой массив с зазубринами.

Худший; поскольку ссылки больше в x64, для массивов типа ref вы фактически получаете меньше элементов в одном массиве.

См. здесь:

Я получил ряд запросов о том, почему 64-разрядная версия среды выполнения 2.0 .Net по-прежнему имеет максимальный размер массива, ограниченный 2 ГБ. Учитывая, что в последнее время это кажется актуальной темой, я придумал небольшую предысторию и обсуждение возможностей обойти это ограничение было целесообразным.

Сначала немного предыстории; в версии 2.0 среды выполнения .Net (CLR) мы приняли сознательное дизайнерское решение сохранить максимальный размер объекта, разрешенный в куче сборщика мусора, равным 2 ГБ, даже в 64-разрядной версии среды выполнения. Это то же самое, что и текущая реализация 32-разрядной среды CLR 1.1, однако вам будет сложно фактически выделить объект размером 2 ГБ в 32-разрядной среде CLR, потому что виртуальное адресное пространство просто слишком фрагментировано, чтобы реально найти 2 ГБ отверстие. Обычно люди не особо озабочены созданием типов, размер которых при создании экземпляров будет> 2 ГБ (или где-то рядом), однако, поскольку массивы - это просто особый вид управляемого типа, который создается в управляемой куче, они также страдают от этого ограничения.


Следует отметить, что в .NET 4.5 ограничение размера памяти необязательно снимается с помощью gcAllowVeryLargeObjects, однако это не меняет максимальный размер измерения. Ключевым моментом является то, что если у вас есть массивы нестандартного типа или многомерные массивы, то теперь вы можете выйти за пределы 2 ГБ памяти.

person Marc Gravell    schedule 21.02.2009
comment
Очень интересно, если это правда, мне интересно, каково оправдание существования свойства Array.LongLength. - person Tamas Czinege; 21.02.2009
comment
Предположительно необходимо получить элементы размером от 1 до 2 ГБ (при условии, что byte []), поскольку int подписан, и они не хотели использовать uint из-за соответствия CLS. - person Marc Gravell; 22.02.2009

Вам вообще не нужен такой большой массив.

Когда ваш метод сталкивается с проблемами ресурсов, не просто смотрите на то, как расширить ресурсы, посмотрите также на метод. :)

Вот класс, который использует буфер размером 3 МБ для вычисления простых чисел с помощью сита Эратосфена. Класс отслеживает, насколько вы вычислили простые числа, и когда диапазон необходимо расширить, он создает буфер для проверки еще 3 миллионов чисел.

Он сохраняет найденные простые числа в списке, и когда диапазон расширяется, предыдущие простые числа используются для исключения чисел в буфере.

Я провел небольшое тестирование, и буфер около 3 МБ наиболее эффективен.

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;
   }

}
person Guffa    schedule 21.02.2009
comment
С точки зрения эффективности, я думаю, было бы более эффективно, если бы размер вашего блока был выровнен с некоторой степенью 2 (например, 3 МБ == 3 * 1024 * 1024), потому что это упростило бы управление памятью для ОС (например, потому что ваша память делится поровну на страницы). - person Hosam Aly; 10.03.2009
comment
Разве не было бы эффективнее использовать наборы битов вместо логических массивов? Это могло как минимум сэкономить много места. - person Hosam Aly; 10.03.2009
comment
@HosamAly: Это не важно, поскольку мы находимся в управляемом пространстве. - person mafu; 19.03.2012
comment
@HosamAly: Экономия места не является проблемой (несколько мегабайт - это ничто), но стоимость доступа к битовым массивам довольно велика. Здесь действительно применимо "Упрощение". - person mafu; 19.03.2012

Я считаю, что даже в 64-битной среде CLR существует ограничение в 2 ГБ (или, возможно, 1 ГБ - я точно не могу вспомнить) на объект. Это помешает вам создать массив большего размера. Тот факт, что Array.CreateInstance принимает только аргументы Int32 для размеров, тоже наводит на размышления.

В более широком смысле, я подозреваю, что если вам нужны массивы такого размера, вы действительно должны изменить подход к решению проблемы.

person Jon Skeet    schedule 21.02.2009
comment
хорошо, я надеялся получить от тебя ответ: D - person inspite; 21.02.2009
comment
В одном вопросе вам нужно получить простые числа до 50 миллиардов, но эффективный способ - использовать Сито Эратосфена, которое заставляет вас объявить массив с таким индексом .. en.wikipedia.org/wiki/Sieve_of_Eratosthenes - person Canavar; 21.02.2009
comment
Я бы сказал, что на данный момент это не эффективный способ. - person Jon Skeet; 22.02.2009

Я новичок в C # (т.е. изучаю его на этой неделе), поэтому я не уверен в точных деталях того, как реализован ArrayList. Однако я предполагаю, что, поскольку вы не определили тип для примера ArrayList, тогда массив будет выделен как массив ссылок на объекты. Это вполне может означать, что вы фактически выделяете 4-8 ГБ памяти в зависимости от архитектуры.

person Jason Waring    schedule 22.02.2009
comment
Хороший момент, логические значения занимают 4 байта в .NET, поэтому 2 ГБ логических значений составляют всего 8 ГБ. Класс ArrayList реализован внутри как массив, который перераспределяет новый (больший) массив по мере необходимости для размещения больших размеров: msdn.microsoft.com/en-us/library/. - person Mike Rosenblum; 22.02.2009
comment
На самом деле он использует гораздо больше. В массиве bool каждый bool использует только один байт, но в ArrayList каждый bool использует 16 байтов. Каждая ссылка составляет 4 байта, каждый объект, упаковывающий bool, имеет два внутренних указателя и 4 байта для bool. Таким образом, ArrayList с 2 миллионами логических значений использует 32 ГБ памяти. - person Guffa; 22.02.2009
comment
@Guffa - или хуже снова на x64, так как ссылок больше ;-p - person Marc Gravell; 22.02.2009
comment
@Mark - правильно, я просто хотел, чтобы это было просто, а количество комментариев ограничено. :) - person Guffa; 25.02.2009
comment
Я использовал Java в течение многих лет, поэтому подумал, что это может быть так. Приятно прояснить мои подозрения. Слои абстракции очень полезны, но иногда нам нужно знать детали реализации :-) - person Jason Waring; 27.02.2009

Согласно 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;
    }
person Anton K    schedule 15.09.2014