Что происходит со стеком вызовов (call stack) во время выполнения метода в C#?

«Что происходит со стеком вызовов (call stack) во время выполнения метода в C#?» — вопрос из категории Управление памятью, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При вызове метода в стеке вызовов (call stack) создается новый кадр стека (stack frame). Этот кадр содержит всю информацию, необходимую для выполнения метода и последующего возврата.

Содержимое кадра стека:

  • Аргументы метода, передаваемые по значению (копии примитивов и структур).
  • Локальные переменные метода (значимые типы хранятся непосредственно, для ссылочных типов хранится только ссылка, сам объект — в куче).
  • Адрес возврата — указатель на код, который должен выполниться после завершения метода.
  • Служебная информация (указатель на предыдущий кадр, сведения для отладки).

Пример:

int Calculate(int a, int b) // 'a' и 'b' помещаются в кадр стека
{
    int result = a + b; // Локальная переменная 'result' размещается в кадре стека
    return result;
}
// При вызове Calculate(5, 3) создается кадр стека с a=5, b=3, result.

Жизненный цикл кадра стека:

  1. Вызов метода: Кадр помещается (push) на вершину стека.
  2. Выполнение метода: Все операции используют память этого кадра.
  3. Завершение метода: Кадр снимается (pop) со стека. Память для его локальных переменных и аргументов мгновенно освобождается (стек просто "забывает" этот участок).

Критические моменты:

  • StackOverflowException возникает, если глубина вызовов методов (количество кадров) превышает лимит стека (обычно ~1 МБ). Частая причина — бесконечная или очень глубокая рекурсия.
  • Значимые типы (struct), объявленные как локальные переменные, хранятся непосредственно в кадре стека и уничтожаются с ним.
  • Ссылочные типы (class) сами хранятся в куче. В кадре стека находится только ссылка (указатель) на объект в куче.