Анализ побега в.NET CLR VM

Есть ли какой-либо escape-анализ, выполняемый компилятором /JIT CLR? Например, в Java кажется, что переменная цикла - объект, размещенный в цикле, который не выходит из цикла, размещается в стеке, а не в куче (см. Анализ Escape в Java).

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

class Foo 
{ 
   int number;
   Foo(int number) { this.number = number; }
   public override string ToString() { return number.ToString(); }
}

for (int i = 0; i < 10000000; i++)
{
   Foo foo = new Foo(i);
   Console.WriteLine(foo.ToString());
}

3 ответа

Решение

Если вы имеете в виду объект (new Foo(i);), тогда я понимаю, что нет: это никогда не выделяется в стеке; однако, он умрет в нулевом поколении, поэтому будет очень эффективным для сбора. Я не признаю, что знаю каждый темный и сырой угол CLI, но я не знаю ни одного сценария в C#, который привел бы к тому, что управляемый ссылочный тип был бы выделен в стеке (такие вещи, как stackalloc на самом деле не считается, и очень специфичны). Очевидно, что в C++ у вас есть еще несколько вариантов, но тогда это не управляемый экземпляр.

Интересно, что в MonoTouch/AOT он может быть собран немедленно, но это не основная виртуальная машина CLI (и для очень специфического сценария).

Что касается переменной - которая обычно будет в стеке (и повторно используется для каждой итерации цикла) - но это может быть не так. Например, если это "блок итератора", то все не удаленные локальные переменные фактически являются полями конечного автомата, созданного компилятором. Чаще всего, если переменная "захвачена" (в анонимный метод или лямбда-выражение, оба из которых образуют замыкания), тогда переменная преобразуется в поле сгенерированного компилятором контекста захвата и является отдельной для каждой итерации цикла (поскольку foo объявлен внутри цикла). Это означает, что каждый отдельный в куче.

Что касается i (переменная цикла) - если это захватывается, становится еще интереснее

  • в C# 1.2 захваты не существовали, но по спецификации переменная цикла технически является итерацией
  • в C# 2.0 до 4.0 переменная цикла используется совместно (вызывая печально известный вопрос перехвата /foreach)
  • в C# 5.0 и выше переменная цикла снова за итерацией

это имеет значение только при захвате переменной, но изменяет семантику того, как именно она проявляется в контексте захвата

Тип значения может быть размещен в стеке (не всегда), но это не относится к экземплярам ссылочных типов. По факту:

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

(Эрик Липперт: Правда о типах значений)

Кроме того, "Стек - это деталь реализации" делает хорошее чтение.

Несмотря на то, что x86 JIT хорошо умеет вставлять значения типов, ваш фрагмент не будет считаться ToString Метод будет виртуальным вызовом в штучной упаковке объекта. Изменить: это может быть не так, так как вы не переопределяете ToString,

Однако x64 JIT не делает этого вообще из моих экспериментов.

Редактировать:

Если возможно, протестируйте свой код на x86 и x64.

Другие вопросы по тегам