Почему эта рекурсия не создает исключение 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 выдадут ошибку, если они слабость в управлении памятью и расписании процессора.