Последствия размещения кучи и стека (.NET) - PullRequest
20 голосов
/ 25 января 2009

Из ответа SO 1 о куче и стеке возник вопрос: почему важно знать, где расположены переменные?

На другой ответ кто-то указал, что стек быстрее. Это единственный вывод? Может ли кто-нибудь привести пример кода, в котором простое изменение местоположения может решить проблему (например, производительность)?

Обратите внимание, что этот вопрос относится к .NET

1 вопрос снят с SO.

Ответы [ 7 ]

16 голосов
/ 25 января 2009

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

Например, JIT может заметить, что вновь созданный объект никогда не использовался вне текущего метода (ссылка никогда не может быть исключена в другом месте), и разместить его в стеке. В данный момент он этого не делает, но это будет законно.

Аналогично, компилятор C # может принять решение о выделении всех локальных переменных в куче - стек будет просто содержать ссылку на экземпляр MyMethodLocalVariables, и через него будет реализован весь доступ к переменным. (Фактически, переменные, захваченные делегатами или блоками итераторов, уже ведут себя подобным образом.)

Ваш вопрос возник, когда Эрик Липперт просматривал C # в Depth - у меня есть раздел, объясняющий, что происходит в C # 1, и он счел, что разработчикам не должно быть все равно .

5 голосов
/ 25 января 2009

( edit: Мой оригинальный ответ содержал упрощенное "структуры размещены в стеке", а также немного запутал stack-vs-heap и value-vs-reference, потому что они связаны в C #. )

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

public class Foo
{
   public int X = 0;
}

Теперь рассмотрим следующий код:

Foo foo = new Foo();
Foo foo2 = foo;
foo2.X = 1;

В этом примере foo и foo2 являются ссылками на один и тот же объект. Установка X на foo2 также повлияет на foo1. Если мы изменим класс Foo на struct, то это уже не так . Это потому, что структуры не доступны через ссылки. Назначение foo2 фактически сделает копию.

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

3 голосов
/ 11 августа 2011

По моему мнению, знание различий между стеком и кучей и тем, как на нем размещаются объекты, может быть очень полезным, когда вы действительно задумаетесь о производительности вашего приложения. Следующие вопросы делают необходимым понять различия: Как вы думаете, что быстрее и эффективнее для доступа .NET? - стек или куча. В каких сценариях .NET может размещать тип значения кучи?

3 голосов
/ 25 января 2009

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

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

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

Это связано с обработкой выделения кучи для C / C ++. На самом деле в .NET распределение кучи происходит довольно быстро (за исключением случаев, когда он запускает сборщик мусора), поэтому даже если вы решите, куда распределить, я думаю, что разница не будет существенной.

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

3 голосов
/ 25 января 2009

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

2 голосов
/ 20 июня 2018

Вопреки распространенному мнению, между стеками и кучами в .NET-процессе не так уж много различий. Стеки и кучи являются не чем иным, как диапазонами адресов в виртуальной памяти, и нет никакого внутреннего преимущества в диапазоне адресов, зарезервированных для стека конкретного потока, по сравнению с диапазоном адресов, зарезервированных для управляемой кучи. Доступ к области памяти в куче не является ни быстрым, ни медленным, чем доступ к области памяти в стеке. Существует несколько соображений, которые в некоторых случаях могут поддерживать утверждение о том, что доступ к памяти в расположениях стека быстрее, в целом, чем доступ к памяти к куче мест. Среди них:

  1. В стеке локальность временного распределения (распределения, сделанные близко друг к другу во времени), подразумевает пространственную локальность (хранилище, которое близко друг к другу в пространстве). В свою очередь, когда локальность временного выделения подразумевает локальность временного доступа (объекты, выделенные вместе, доступны вместе), хранилище последовательного стека имеет тенденцию работать лучше в отношении кэшей ЦП и систем подкачки операционной системы.
  2. Плотность памяти в стеке имеет тенденцию быть выше, чем в куче из-за издержек ссылочного типа. Более высокая плотность памяти часто приводит к повышению производительности, например, из-за того, что больше объектов помещается в кэш ЦП.
  3. Стеки потоков имеют тенденцию быть довольно маленькими - максимальный размер стека по умолчанию в Windows составляет 1 МБ, и большинство потоков, как правило, используют только несколько страниц стека. В современных системах стеки всех потоков приложений могут помещаться в кэш ЦП, что делает типичный доступ к объектам стека чрезвычайно быстрым. (С другой стороны, целые кучи редко помещаются в кэши ЦП.)

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

0 голосов
/ 18 марта 2019

Я много играл с разными бенчмарками в стеке и куче, и я бы сделал следующие выводы:

Аналогичная производительность для

  • небольших приложений (не слишком много объектов в куче)
  • объекты размером <1 КБ </li>

Более высокая производительность стека для (1x - 5x быстрее)

  • Объекты размером от 1КБ до 100КБ

Гораздо лучшая производительность для (до 100 раз быстрее или даже больше)

  • Большое количество объектов
  • Большое давление памяти - много выделений в секунду и полная память
  • большие объекты 10 КБ - 3 МБ (я полагаю, системы x64)
  • XBox (медленный сборщик мусора)

Лучший подход - объединение массивов. Он быстр как стек, но у вас нет такого ограничения, как со стеком.

Еще одним следствием использования стека является то, что он является поточно-ориентированным благодаря desing.

Ограничение памяти по умолчанию для стека в 64-разрядной версии Windows составляет 4 МБ. Таким образом, вы будете в безопасности, выделяя не более 3 МБ.

...