Исключение StackOverflow при инициализации массива - PullRequest
1 голос
/ 25 июня 2019

Я вызываю метод и получаю StackOverflowException. Это не рекурсивный вызов, а просто инициализация массива. Мне нужен массив BigIntegers, код отлично работает с массивом int даже гораздо большего размера. Я показываю упрощенный пример, и в реальном коде я не могу использовать цикл для заполнения массива, поскольку я не могу генерировать нужные мне числа, поэтому мне приходится жестко их кодировать.

Настройка: Режим x64, .Net Core

Из подробности ошибки мы видим, что:

1) Трассировка стека равна нулю

2) Ошибка предположительно возникла в System.Collections.ListDictionaryInternal

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Before"); // <--- This is displayed

            var a = GetBigIntegers(); // <--- Mehtod is called

            Console.WriteLine("After"); // <--- We will never get there
        }


        static BigInteger[] GetBigIntegers()
        {
            // <--- Crash here
            return new BigInteger[]
            {
                1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                // Many more lines (850-900) and they are 2-3 times longer than here
                1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
            };
        }
    }

Я проверил код IL, он выглядит правильно и занимает около 400 000 строк.

.method private hidebysig static 
    valuetype [System.Runtime.Numerics]System.Numerics.BigInteger[] GetBigIntegers () cil managed 
{
    // Method begins at RVA 0x207c
    // Code size 1130123 (0x113e8b)
    .maxstack 4
    .locals init (
        [0] valuetype [System.Runtime.Numerics]System.Numerics.BigInteger[]
    )

    // (no C# code)
    IL_0000: nop
    IL_0001: ldc.i4 66500
    IL_0006: newarr [System.Runtime.Numerics]System.Numerics.BigInteger
    IL_000b: dup
    IL_000c: ldc.i4.0
    //  return new BigInteger[66500]IL_000d: ldc.i4.1
    IL_000e: call valuetype [System.Runtime.Numerics]System.Numerics.BigInteger [System.Runtime.Numerics]System.Numerics.BigInteger::op_Implicit(int32)
    // (no C# code)
    IL_0013: stelem [System.Runtime.Numerics]System.Numerics.BigInteger
    IL_0018: dup
    IL_0019: ldc.i4.1
    IL_001a: ldc.i4.1
    IL_001b: call valuetype [System.Runtime.Numerics]System.Numerics.BigInteger [System.Runtime.Numerics]System.Numerics.BigInteger::op_Implicit(int32)
    IL_0020: stelem [System.Runtime.Numerics]System.Numerics.BigInteger
.....
    IL_113e75: dup
    IL_113e76: ldc.i4 66499
    IL_113e7b: ldc.i4.1
    IL_113e7c: call valuetype [System.Runtime.Numerics]System.Numerics.BigInteger [System.Runtime.Numerics]System.Numerics.BigInteger::op_Implicit(int32)
    IL_113e81: stelem [System.Runtime.Numerics]System.Numerics.BigInteger
    IL_113e86: stloc.0
    IL_113e87: br.s IL_113e89

    IL_113e89: ldloc.0
    IL_113e8a: ret
} // end of method Program::GetBigIntegers

Я ожидал, что массив будет инициализирован и возвращен, но на самом деле я получил ошибку StackOverflow.

Я знаю, что могу использовать разные подходы, чтобы делать одно и то же, но я хочу знать, ПОЧЕМУ это не работает таким образом. Надеюсь, это будет интересно всем, кто читает этот вопрос.

Ответы [ 2 ]

1 голос
/ 26 июня 2019

Фактическая причина в том, что размер фрейма стека оценки недостаточно велик для того, чтобы в него помещалось все.

Причиной этого является скрытие оптимизаций JIT-компилятора, которые не выполняются для инициализации структуры внутри больших методов.(что приводит к генерированию машинного кода с плохой производительностью).

Источник .

0 голосов
/ 25 июня 2019

.NET ограничивает массивы 2 ГБ элементов (или байтов?).Поскольку он использует 32-разрядное целое число со знаком для индекса (максимальный индекс System.Int32.MaxValue).

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

С наилучшими пожеланиями.

...