Почему размер стека в C# ровно 1 МБ?

сегодняшние ПК имеют большое количество физической ОЗУ, но все же размер стека C# составляет всего 1 МБ для 32-разрядных процессов и 4 МБ для 64-разрядных процессов (емкость стека в C#).

Почему размер стека в CLR все еще настолько ограничен?

и почему именно 1 Мб (4 МБ) (а не 2 Мб или 512 КБ)? Почему было принято решение использовать эти суммы?

меня интересует:соображения и причины этого решения.

2 ответов


enter image description here

вы смотрите на парня, который сделал этот выбор. Дэвид Катлер и его команда выбрали один мегабайт в качестве размера стека по умолчанию. Ничего общего с .NET или C#, это было прибито, когда они создали Windows NT. Один мегабайт-это то, что он выбирает, когда заголовок EXE программы или вызов CreateThread() winapi явно не указывает размер стека. Это нормальный способ, почти любой программист оставляет его в ОС, чтобы выбрать размер.

что выбор, вероятно, предшествует дизайну Windows NT, история слишком мутная об этом. Было бы неплохо, если бы Катлер написал об этом книгу, но он никогда не был писателем. Он оказал огромное влияние на работу компьютеров. Его первой ОС была RSX-11M, 16-разрядная операционная система для компьютеров DEC (Digital Equipment Corporation). Это сильно повлияло на CP/M Гэри Килдалла, первую приличную ОС для 8-битных микропроцессоров. Что сильно повлияло на MS-DOS.

его следующей дизайн был VMS, операционная система для 32-разрядных процессоров с поддержкой виртуальной памяти. Очень удачный. Его следующий был отменен DEC примерно в то время, когда компания начала распадаться, не имея возможности конкурировать с дешевым оборудованием для ПК. Cue Microsoft, они сделали ему предложение, от которого он не мог отказаться. Многие из его коллег тоже присоединились. Они работали на VMS v2, более известной как Windows NT. Дек расстроился, деньги перешли из рук в руки. Является ли VMS уже выбран один мегабайт что-то я не знаю, я знаю только RSX-11 достаточно хорошо. Это не маловероятно.

достаточно истории. Один мегабайт-это много, реальный поток редко потребляет больше, чем пару горстей килобайт. Так что мегабайт-это довольно расточительно. Это, однако, вид отходов, которые вы можете себе позволить в операционной системе виртуальной памяти с подкачкой по требованию, что мегабайт просто виртуальный. Просто цифры для процессора, по одному на каждые 4096 байт. Ты никогда ... фактически используйте физическую память, ОЗУ в машине, пока не обратитесь к ней.

это чрезмерно в программе .NET, потому что один размер мегабайта был первоначально выбран для размещения собственных программ. Которые, как правило, создают большие фреймы стека, сохраняя строки и буферы (массивы) в стеке. Печально известный как вектор атаки вредоносных программ, переполнение буфера может манипулировать программой с данными. Не так, как работают программы .NET, выделяются строки и массивы на GC проверяется куча и индексирование. Единственный способ выделить место в стеке с помощью C# - это небезопасное stackalloc ключевое слово.

единственным нетривиальным использованием стека в .NET является дрожание. Он использует стек вашего потока для своевременной компиляции MSIL в машинный код. Я никогда не видел и не проверял, сколько места требуется, это скорее зависит от характера кода и включен ли оптимизатор, но пара десятков килобайт-это догадываться. В противном случае, как этот сайт получил свое имя, переполнение стека в программе .NET довольно фатально. Осталось недостаточно места (менее 3 килобайт), чтобы надежно JIT любой код, который пытается поймать исключение. Бабах на рабочий стол является единственным вариантом.

и последнее, но не менее важное: программа .NET делает что-то довольно непродуктивное со стеком. CLR будет фиксация стопка нити. Это дорогое слово, которое означает, что оно не просто зарезервируйте размер стека, он также гарантирует, что пространство зарезервировано в файле подкачки операционной системы, чтобы стек всегда можно было заменить при необходимости. Неспособность зафиксировать является фатальной ошибкой и завершает программу безоговорочно. Это происходит только на машине с очень маленьким ОЗУ, которая запускает слишком много процессов, такая машина превратится в патоку, прежде чем программы начнут умирать. Возможная проблема 15+ лет назад, а не сегодня. Программисты, которые настраивают свои программы как гоночный автомобиль F1 используйте <disableCommitThreadStack> элемент в их .конфигурационный файл.

Fwiw, Катлер не прекратил разработку операционных систем. Эта фотография была сделана, когда он работал над Azure.


Update, я заметил, что .NET больше не фиксирует стек. Не совсем уверен, когда и почему это произошло, прошло слишком много времени с тех пор, как я проверял. Я предполагаю, что это изменение дизайна произошло где-то около .NET 4.5. Довольно разумная перемена.


размер зарезервированного стека по умолчанию задается компоновщиком, и он может быть переопределен разработчиками путем изменения значения PE во время ссылки или для отдельного потока, указав на CreateThread функция WinAPI.

Если вы создаете поток с начальным размером стека, большим или равным размеру стека по умолчанию, то он округляется до ближайшего кратного 1 МБ.

почему значение равно 1 МБ для 32-разрядных процессов и 4 МБ для 64-бит? Я думаю, вы должны спросить разработчиков, которые разработали Windows, или ждать, пока кто-то из них ответит на ваш вопрос.

вероятно, Марк Русинович знает это, и вы можете контакты его. Возможно, вы можете найти эту информацию в его книгах Windows Internals раньше, чем шестое издание, которое описывает меньше информации о стеках, а не его статьи. Или, может быть, Раймонд Чен знает причины, так как он пишет интересные вещи о Windows internals и его история. Он может ответить на ваш вопрос, но вы должны разместить предложение Ящик Для Предложений.

но в это время я попытаюсь объяснить некоторые вероятные причины, почему Microsoft выбрала эти значения, используя блоги MSDN, Марка и Раймонда.

значения по умолчанию имеют эти значения, вероятно, потому, что в ранние времена ПК были медленными, а выделение памяти в стеке было намного быстрее, чем выделение памяти в куче. И поскольку распределение стека было намного дешевле они использовались, но для этого требовался больший размер стека.

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

В настоящее время эти значения в основном используются для обратной совместимости, потому что структуры, которые передаются в качестве параметров для функций WinAPI, все еще выделено в стеке. Но если вы не используете распределения стека, то использование стека потока будет значительно меньше, чем 1 МБ по умолчанию, и это расточительно, как упоминал Ханс Пассант. И для предотвращения этого ОС фиксирует только первую страницу стека (4 КБ), если в заголовке PE приложения не указано другое. Другие страницы выделяются по запросу.

некоторые приложения переопределяют зарезервированное адресное пространство и первоначально обязуются оптимизировать использование памяти. В качестве примера, максимальный размер стека потока собственного процесса IIS составляет 256 КБ (KB932909). И это уменьшение значений по умолчанию -рекомендовано по Microsoft:

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

источники:

  1. размер стека потоков (MSDN)
  2. Раздвигая границы Windows: процессы и потоки (Марк Русинович)
  3. по умолчанию максимальный размер стека потока, созданного в собственном процессе IIS, составляет 256 КБ (KB932909)