#c_sharp #структуры #сборщик_мусора #производительность
Недавно прочитал статью Предельная производительность: C#
Из-за того, что структуры хранятся в stack’е, они не требуют сборки мусора
поясните пожалуйста, это действительно так?
Ответы
Ответ 1
Краткое содержание. Нет, структуры не обязательно хранятся в стеке, а объекты --
в куче. Да, с хорошими шансами структура всё же попадёт в стек. Нет, вам не стоит на
это рассчитывать, пользоваться этим и пытаться оптимизировать таким образом.
На самом деле следует понимать простую вещь: выделение переменной на стеке дешевле,
поскольку её можно легко уничтожить, не включая в цикл сборки мусора. Выгода на самом
деле только в этом. (Аллокация что на стеке, что в куче -- не более, чем увеличение
одного указателя, она очень быстрая.)
Очевидно, оптимизатор будет размещать в стеке те переменные, которые, как он может
доказать, не нужны после смерти текущего фрейма. Часто про структуры можно такое доказать,
но не всегда. Например, структура может быть частью объекта класса, и должна умереть
вместе с классом. Или метод будет неявно переписан в стиле продолжений, например, если
это генератор (yield return & Co.) или Task<> с async/await. Или переменная попала
в замыкание некоторой лямбда-функции. И так далее. Но обычно структуры не нужны после
отработки метода, так что оптимизатор может вытеснить их в стек.
С другой стороны, про некоторые объекты можно тоже утверждать, что они не нужны после
окончания фрейма -- и тогда оптимизатор тоже имеет полное право (но не обязанность,
конечно) разместить и их на стеке.
Обратите внимание на такую тонкость: если вы возвращаете из метода структуру, вы
на самом деле возвращаете её копию, поэтому структура, с которой вы работали, может
попасть в стек. С классами же не так: они копируются не по значению, а по ссылке, поэтому
возвращаемый объект переживает создавшую его функцию, и следовательно не имеет права
жить в стеке.
Использованы материалы из блога Эрика Липперта, на которые была ссылка выше.
Добавлю ещё пару цитат из Эрика:
Использование стека для локальных переменных-структур -- всего лишь оптимизация,
которую CLR выполняет для вас. Существенная особенность структур -- семантика копирования
по значению, а вовсе не то, что в некоторых случаях их уничтожение может быть оптимизировано
рантайм-библиотекой.
В подавляющем большинстве программ, выделение и уничтожение локальных переменных
не будут критически важным фактором производительности.
Превращение типа, который должен на самом деле быть ссылочным типом, в структуру
-- это нано-оптимизация, дающая выгоду в пару наносекунд, и вероятно не стоящая того.
На вашем месте я бы проводил такую оптимизацию только если данные профилирования покажут,
что существует реальная, большая проблема у ваших реальных клиентов, которую можно
исправить использованием структур. Не имея таких данных на руках, я всегда бы делал
выбор между классами и структурами основываясь на том, представляет ли тип семантически
значение или ссылку на что-то. (То есть, имеет ли объект смысл помимо значения, содержащегося
в нём, обладает ли он самостоятельной сущностью -- VladD)
Ответ 2
Из-за того, что структуры хранятся в stack’е, они не требуют сборки мусора
поясните пожалуйста, это действительно так?
Если речь идет о локальных переменных, то это действительно так. GC работает с кучей.
Очевидно, что структуры не всегда хранятся в стеке. Например, если какой-то класс
содержит поля, являющиеся структурами, то память под них однозначно будет выделена
в куче, и, следовательно, будут уничтожаться GC.
P.S.: Если структура содержит управляемые поля, то память под эти поля выделится
в куче, а в стеке окажутся лишь ссылки на эти управляемые поля. Довольно логично, но
некоторые упускают это из виду.