Почему эта рекурсия не создает исключение StackOverFlowException?

Что не так с этим кодом:

using System;
namespace app1
{
    static class Program
    {
        static int x = 0;
        static void Main()
        {
            fn1();
        }
        static void fn1()
        {
            Console.WriteLine(x++);
            fn1();
        }
    }
}

Я скомпилирую этот фрагмент кода с помощью этой команды:

csc /warn:0 /out:app4noex.exe app4.cs

Когда я дважды нажимаю на exe, он, похоже, не генерирует исключение (StackOverFlowException) и продолжает работать навсегда.

Использование командной строки Visual Studio 2010, но у меня также есть vs 2012, установленный в системе, все в актуальном состоянии.

Ответ 1

Поскольку оптимизатор разворачивает вызов хвостовой рекурсии на:

    static void fn1()
    {
      START:

        Console.WriteLine(x++);
        GOTO START;
    }

Перепишите, чтобы получить такие исключения:

   static int y;

   static void fn1()
   {
       Console.WriteLine(x++);
       fn1();
       Console.WriteLine(y++);
   }

Ответ 2

Джиттер x64 обнаруживает это как хвостовой вызов и оптимизирует его, в то время как джиттер x86 этого не делает. Дисктер x64 более агрессивен в отношении этих optmizations. См. Bart de Smet анализ и команда CLR сообщение в блоге в он.

Ответ 3

Существует такая вещь, которая называется хвостовая рекурсивная оптимизация.

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

static void Main()
{
  fn(0);
}

static void fn(int value)
{
   fn(value+1);
}

вместо стека вызовов, растущего Main->fn(0)->fn(1)->... ad nauseam, он будет иметь ровно две ссылки длинными, сначала Main->fn(0), чем Main->fn(1), до Main->fn(int.MaxValue), где он будет либо взорваться, либо переполняться.

Теперь, вопрос в том, действительно ли компилятор С# делает это?
AFAIK, используя компиляторы версии 4.0 и более поздней версии, при компиляции в среде x86 он не использует оптимизацию хвостового вызова, а при компиляции приложения x64 он использует хвост оптимизация (и, по-видимому, из других комментариев/ответов я прав). Например. В моей системе, используя LINQPad, код, который вы предоставили, быстро взорвался с помощью StackOverflowException.

Ответ 4

Когда программа работает в среде визуальной студии, она будет использовать ограниченный стек. Я имею в виду, что когда вы компилируете программу в VS2012, нажимая F5 и F6 на клавиатуре, она отправит некоторые параметры в программу csc.exe, чтобы ограничить программу и вызвать переполнение стека, чтобы вы знали об ошибке, которая входит в вашу программу исходный код и алгоритм. На самом деле, нет ошибки стека с потоком, программный процесс будет использовать реальное хранилище и виртуальное хранилище, а ОС сделает это за вас.

Примечание: это также связано с вашим os, некоторые os выдадут ошибку, если они слабость в управлении памятью и расписании процессора.