Распределение кучи и стека (.NET)

17

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

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

Обратите внимание, что этот вопрос специфичен для .NET

1 вопрос удаляется из SO.

    
задан Jader Dias 25.01.2009 в 04:13
источник
  • Примечание для будущих Googlers, первая ссылка в этом вопросе c # нарушена (как указано), а вторая ссылка - о распределении стека вообще и не относится к .NET. –  User453465436754 29.03.2015 в 04:06
  • Как отметил Уоррен Пэ с его 66-ми полезными голосами (на момент написания): «Приятно ли указывать, что успех современных языков с« управляемым кодом »- это успех осознания стека и кучи необходимо (много) для разработчиков C # и Java, потому что те из нас, кто должен отлаживать вещи, которые мы сделали не так на C и C ++, должны были понять модель, чтобы мы могли исправить наш код, когда он ломается. Другими словами, «невежество - это сила ', потому что абстракция работает так, как она была разработана, что позволяет мне работать на виртуальной машине, которая проще в использовании, чем реальная ». –  User453465436754 29.03.2015 в 04:12

5 ответов

16

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

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

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

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

ответ дан Jon Skeet 25.01.2009 в 08:40
  • Эрик - человек. Вам повезло, что у вас такой хороший рецензент. –  RichardOD 09.03.2010 в 11:21
5

( edit: В моем первоначальном ответе содержались упрощенные «структуры, выделенные в стеке», а путанные ссылки на стек-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 на структуру, это уже не так. . Это связано с тем, что структуры не доступны через ссылки. Присвоение foo2 действительно сделает копию.

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

ответ дан Wim Coenen 25.01.2009 в 04:42
  • Пока ваш пример и объяснение верны, вы продемонстрировали контрпример к вашему первому предложению. Что такое "Foo.X"? Int, который является типом значения - и все же он всегда живет в куче. Требование «классы распределены по кучам, структуры, выделенные стеком», является упрощенным. –  Jon Skeet 25.01.2009 в 08:41
  • Лучшая формулировка будет заключаться в том, что типы значений выделяются там, где они объявлены. Если они объявлены как локальные переменные в функции, они находятся в стеке. Если они объявлены членами в классе, они находятся в куче, как часть этого класса. –  jalf 25.01.2009 в 10:45
  • Я не буду проголосовать за этот ответ, потому что он смешивает ссылки на значения с последствиями кучи и стека. –  Jader Dias 25.01.2009 в 13:40
  • Спасибо за исправление, оно вызвало «Ага!». момент. Теперь я понимаю, что stack-vs-heap является относительно несущественной деталью реализации в C #, и эта ценность-vs-reference является реальной проблемой здесь. Я должен был объяснить это так. –  Wim Coenen 25.01.2009 в 15:00
  • теперь я голосовал из-за ваших исправлений –  Jader Dias 25.01.2009 в 15:30
Показать остальные комментарии
3

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

    
ответ дан DavGarcia 25.01.2009 в 04:20
3

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

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

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

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

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

ответ дан Brian Rasmussen 25.01.2009 в 14:58
  • Спасибо за это фундаментальное замечание: в .NET мало что обсуждать, поскольку он не является пользователем типа, который решает, где распределять экземпляры, тогда как в таких языках, как C или C ++, пользователь может решить, где распределяются данные и для некоторых особых случаев он может быть значительно быстрее выделять из стека по сравнению с распределением из кучи. –  Veverke 18.07.2016 в 14:46
3

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

    
ответ дан Ankit Dass 11.08.2011 в 15:45