Зачем в C# разделение памяти на стек и кучу?
Что проверяют
Понимаете ли вы, что разделение продиктовано временем жизни данных, а не видом типа. Главный фильтр: скажете ли вы «значимые типы всегда на стеке». Следом обычно спрашивают про боксинг, сборщик мусора или Span<T>.
Как отвечать
Объясняйте через время жизни:
- стек: у каждого потока свой, размер ограничен. Хранит кадры вызовов: параметры, локальные переменные, адрес возврата. Выделение это сдвиг указателя, освобождение происходит само при выходе из метода. Это работает, потому что время жизни строго вложенное;
- управляемая куча: объекты, чья жизнь не привязана к вызову: их передают, сохраняют в полях, возвращают. Освобождает их GC с поколениями 0, 1 и 2, объекты от 85 000 байт живут в куче больших объектов (LOH). Сама аллокация в куче тоже дешёвая, дорого обходится сборка;
- где что лежит: экземпляры
classв куче. Значимый тип лежит там же, где его контейнер: локальныйintна стеке или в регистре, полеintвнутри объекта и элементыint[]в куче. Локальные переменные, захваченные лямбдой, а также живущие вasync-методе или итераторе, переезжают в объект на куче.
Зачем это знать на практике: меньше аллокаций в куче, реже сборки и паузы. Отсюда небольшие неизменяемые struct, stackalloc вместе со Span<T>, ref struct, который компилятор не выпустит в кучу, ArrayPool<T>. Боксинг работает в обратную сторону: приведение значения к object или к интерфейсу создаёт объект в куче. Хорошо добавить, что в последних версиях .NET JIT умеет в отдельных случаях размещать на стеке объекты, которые не покидают метод, поэтому само место хранения это деталь реализации.
Где спотыкаются
Говорят «value type на стеке, reference type в куче». Первая половина неверна, вторая верна как деталь реализации.
Считают кучу медленной из-за выделения памяти. Дорого не выделение, а работа GC и промахи кэша при обходе разбросанных объектов.
Делают большие изменяемые struct ради скорости. Они копируются при каждой передаче и присваивании, выигрыш превращается в потери, а изменение копии вместо оригинала даёт трудноуловимые баги.