Почему типы значений хранятся в стеках?

Почему C# (.Net) предпочитает стек для хранения типов значений? Какова основная причина этого дизайна? Это потому, что операции чтения/записи в стек лучше используют процессор машины?

Кроме того, может быть, вы можете обосновать, почему не другие?


person this. __curious_geek    schedule 19.12.2009    source источник


Ответы (6)


Эрик Липперт обсуждает этот здесь; во-первых, неверно, что типы значений хранятся в стеке. Они иногда бывают, но не так:

  • поля в классе
  • захваченные переменные
  • переменные в блоке итератора

Когда они могут храниться в стеке, это удобный способ моделирования их жизненного цикла, но обязательно хранить их в стеке. Например, вы можете написать компилятор+CLI без имеющего стека.

person Marc Gravell    schedule 19.12.2009
comment
См. также блоги. msdn.com/b/ericlippert/archive/2010/09/30/ - person Eric Lippert; 05.10.2011
comment
Статья по ссылке выше великолепна, почему у нее мало голосов? - person Cristian Diaconescu; 19.07.2013
comment
документы. microsoft.com/en-us/archive/blogs/ericlippert/ обновленная ссылка - person Barış Akkurt; 17.11.2020

C# ничего не хранит в стеке. C# — это язык программирования. Следовательно, более правильная версия вашего вопроса: почему компилятор Microsoft C# выдает инструкции CIL для выделения типов значений в стеке?

Ну, во-первых, это бывает только иногда. В стек не попадают:

  1. Типы значений, являющиеся полями в классе
  2. Типы значений в штучной упаковке
  3. Типы локальных значений, являющиеся внешними переменными анонимных методов.
  4. Типы локальных значений, являющиеся внешними переменными блоков итераторов

Во-вторых, когда это возможно, это делается потому, что это эффективно. По сути, в модели памяти CLR освобождение памяти в стеке обходится очень дешево по сравнению с освобождением памяти в куче. С локальными типами значений вы можете быть уверены, что никто, кроме локальных, не будет ссылаться на память, поэтому вы можете избежать использования стека вместо кучи. Дополнительные сведения см. в разделе Эрик Липперт.

Наконец, что делает типы значений особенными, так это то, что они имеют семантику типов значений (копирование по значению), а не то, что они иногда размещаются в стеке. Спецификация C# не требует, чтобы компилятор выдавал инструкции для выделения типов значений в стеке. Спецификация C# требует, чтобы типы значений имели семантику типов значений.

person jason    schedule 19.12.2009

Как указывает @Akash, это в основном связано с памятью. Во время разработки CLR было замечено (моя догадка основывалась на опыте работы с Java), что представление небольших примитивных типов в виде объектов с дескрипторами, подвергаемых сборщику мусора, приводит к большим затратам на отслеживание. Поэтому дизайнеры хотели получить «легкий» объект, за которым не нужно было следить.

В спецификации CLI нет особых требований к размещению примитивов в стеке; это артефакт реализации на машине. Важно то, что среда выполнения знает, где находятся экземпляры, благодаря построению четко определенных шаблонов памяти (называемых кадрами), а не индексу выделенных объектов GC. На машинах x86 (и подобных) это можно эффективно сделать с помощью стека.

person codekaizen    schedule 19.12.2009

Ваше утверждение не совсем верно. Улучшенная версия: C# хранит локальные переменные в стеке.
И в этом нет ничего особенного или нового, (почти) все языки программирования используют стек для локальных переменных и адресов возврата методов. Существует поддержка для этого вплоть до аппаратного обеспечения.

Кроме того, типы значений могут быть локальными переменными или полями внутри ссылочных типов. Таким образом, типы значений не всегда хранятся в стеке. Более полезное утверждение: ссылочные типы никогда не сохраняются в стеке.

Так что не зацикливайтесь на стеке слишком сильно, это деталь реализации. Узнайте о типах значений и ссылок.

person Henk Holterman    schedule 19.12.2009

Операция со стеком или операция с кучей, обе они будут одинаковыми, поскольку вы обращаетесь к адресу памяти, который находится в двух разных местах.

Типы значений small, int, byte и т. д., они небольшие по размеру, и на них очень часто ссылаются с точки зрения математических вычислений. Поскольку они очень малы по размеру, максимум от 4 до 16 байт (вы не должны использовать более 16 байт в типе значения для лучшей производительности), выделение такого небольшого пространства в куче и освобождение, сборка мусора и т. д. было бы очень дорогостоящим.

Для каждого метода, который мы вводим, в среднем мы определяем 10 локальных типов значений для внутреннего использования, что будет очень дорого для кучи в качестве ссылочных типов.

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

Где еще в ссылочном типе каждый ссылочный объект имеет свое собственное распределение и размер, плюс CLR должна поддерживать таблицу объектов, которая является своего рода индексом фактических указателей в памяти, чтобы избежать переполнения буфера. Таким образом, один объект, который вы используете (ссылочный тип), на самом деле имеет два хранилища, одну запись индекса в справочной таблице CLR и фактическое пространство памяти. Вот почему легко и быстро хранить типы значений в стеке.

person Akash Kava    schedule 19.12.2009
comment
Размер стека не может увеличиваться или уменьшаться после того, как он установлен для потока. Вместо этого указатель стека перемещается вверх или вниз в зависимости от того, что находится в стеке. Кроме того, бокс — это не то, что вы описываете. Бокс принимает тип значения и выделяет дескриптор в куче GC. Это может вызвать сбор и поэтому в среднем это не дешево. Распаковка выполняется быстрее, так как она копирует значение из кучи в стек без выделения памяти или какого-либо дополнительного давления сборщика мусора. - person codekaizen; 19.12.2009
comment
Я пояснил, что размер стека, конечно, не увеличивается и не уменьшается, но часть стека, используемая для использования текущего метода, то есть размер всех локальных переменных, может увеличиваться или уменьшаться. Поскольку этот стек не является чистым стеком, в нем есть зарезервированные места для текущего метода. - person Akash Kava; 19.12.2009

Для правильной работы программы важно, чтобы сущности как типа значения, так и типа класса пережили любые ссылки на них. Когда создается объект типа класса, создается ссылка, которую можно свободно копировать в любую область. Следовательно, вполне возможно, что ссылки на объект будут продолжать существовать даже после выхода из текущей области. Напротив, когда создается переменная типа значения, могут быть созданы только ссылки краткосрочного типа, которые исчезнут до выхода из текущей области. Тот факт, что не может существовать ссылка на переменную типа значения при выходе из текущей области видимости, делает безопасным хранение таких переменных в стеке.

person supercat    schedule 29.08.2011