Питання Що і куди стоять і купа?


Мовні книги програмування пояснюють, що ці типи значень створені на стек, і посилання на типи створюються на купа, не пояснюючи, що ці дві речі. Я не читав чіткого пояснення цього. Я розумію, що стек є Але,

  • де і як вони (фізично в реальній пам'яті комп'ютера)?
  • Наскільки вони контролюються операційною системою або мовою середовища?
  • Яка їхня сфера?
  • Що визначає розмір кожного з них?
  • Що робить швидше?

7125
2017-09-17 04:18


походження


дійсно гарне пояснення можна знайти тут Яка різниця між стек і купою? - Songo
Також (насправді) добре: codeproject.com/Articles/76153/... (частина стека / купи) - Ben
Гарне пояснення можна знайти тут - Bharat
youtube.com/watch?v=clOUdVDDzIM&spfreload=5 - Selvamani
Пов'язані, див Стек Clash. Відновлення системи Stack Clash вплинуло на деякі аспекти системних змінних та поведінки, як-от rlimit_stack. Також див. Red Hat Випуск 1463241 - jww


Відповіді:


Стійка - це пам'ять, яку виділяють як скретч-пробіл для потоку виконання. Коли викликається функція, блок зберігається у верхній частині стеку для локальних змінних та деяких бухгалтерських даних. Коли ця функція повертається, блок стає невикористаним і може бути використаний при наступному викликанні функції. Стек завжди зберігається в порядку LIFO (останнє в першому); останній зарезервований блок завжди є наступним блоком, який буде звільнений. Це робить дійсно простим відстеження стеків; звільнення блоку від стека - це не що інше, як налаштування одного покажчика.

Купа - це пам'ять, яку виділяють для динамічного розміщення. На відміну від стеків, не існує належного шаблону для розподілу та вилучення блоків з купи; ви можете виділити блок в будь-який час і безкоштовно його в будь-який час. Це робить набагато складнішим відстеження того, які частини купи виділяються або вільні в будь-який даний момент часу; Є багато власних розподільників динаміків, доступних для настроювання продуктивності купи для різних моделей використання.

Кожен потік отримує стек, тоді як для програми це зазвичай є лише однією купою (хоча це не рідкість для декількох куль для різних видів розподілу).

Щоб відповісти на ваші запитання безпосередньо:

Наскільки вони контролюються операційною системою або мовою середовища?

ОС виділяє стек для кожної нитки на рівні системи при створенні потоку. Як правило, OS запускається під час виконання мови для виділення купи для програми.

Яка їхня сфера?

Стік прикріплений до потоку, тому, коли витікає стек, він повертається. Купа, як правило, виділяється при запуску додатків на час виконання, і відновлюється, коли прикладна програма (технічно процес) виходить.

Що визначає розмір кожного з них? 

Розмір стека встановлюється, коли створюється потока. Розмір купи встановлюється на запуск додатка, але може зростати, оскільки потрібен простір (розподільник вимагає більше оперативної пам'яті).

Що робить швидше?

Стійка швидше, тому що шаблон доступу робить тривіальним, щоб виділити і виділити пам'ять з нього (покажчик / ціле число просто збільшується або зменшується), а куча набагато складнішого бухгалтерського обліку, пов'язаного з розподілом або вилученням. Крім того, кожен байт в стеку, як правило, повторно використовується дуже часто, що означає, що він, як правило, відображається в кеш процесора, що робить його дуже швидким. Інша продуктивність для купи полягає в тому, що куча, як правило, є глобальним ресурсом, як правило, повинна бути безпечною для багатопотоків, тобто кожне розподіл та вилучення повинні бути - як правило, - синхронізованими з "всіма" доступом інших купів у програмі.

Ясна демонстрація:
Джерело зображення: vikashazrati.wordpress.com


5238
2017-09-17 04:52



Хороша відповідь - але я думаю, ви повинні додати, що поки стек розподіляється ОС, коли процес починається (припускаючи існування ОС), він підтримується вбудованим за допомогою програми. Це ще одна причина того, що стека відбувається швидше, а також - операції "push" та "pop" - це, як правило, одна інструкція для машин, і сучасні машини можуть виконувати принаймні 3 з них протягом одного циклу, тоді як виділення або вилучення купи передбачає виклик до коду ОС. - sqykly
Насправді я схвильований діаграмою в кінці. Я думав, що отримав це, поки не побачив це зображення. - Sina Madani
@Anarelle процесор запускає інструкції з або без os. Прикладом, близьким до мого серця, є SNES, який не мав жодних API-викликів, ніякої ОС, як ми її знаємо сьогодні, - але він мав стек. Розподіл на стек - це додавання та віднімання цих систем, і це добре для змінних, що руйнуються, коли вони з'являються, повертаючись із функції, яка їх створила, але конструстувати це, скажімо, конструктору, з якого результат не може бути просто викинути. Для цього нам потрібна куча, яка не прив'язана до дзвінка та повернення. У більшості ОС API є куча, немає підстав зробити це самостійно - sqykly
"стек - це пам'ять, яку виділяють як" "подряпину" ". Прохолодно Але де це фактично "відкладено" з точки зору пам'яті Java? Чи є це купа пам'яті / Non-куч пам'яті / інші (структура пам'яті Java відповідно до betsol.com/2017/06/... ) - Jatin Shashoo


Стек:

  • Зберігається в комп'ютерній ОЗП так само, як куча.
  • Змінні, створені в стекі, вийдуть із сфери застосування та автоматично вилучаються.
  • Набагато швидше виділяти в порівнянні з змінами на купі.
  • Впроваджено фактичну структуру даних стек.
  • Зберігає локальні дані, адреси повернення, які використовуються для передачі параметрів.
  • Може мати переповнення стеків, коли використовується надто велика кількість стеків (переважно з нескінченної або занадто глибокої рекурсії, дуже великих розподілів).
  • Дані, створені на стекі, можуть використовуватися без покажчиків.
  • Ви будете використовувати стек, якщо точно знаєте, скільки даних ви повинні розподілити перед складанням часу, і це не надто велике значення.
  • Зазвичай максимальний розмір вже визначається, коли починається ваша програма.

Купа:

  • Зберігається в комп'ютерній пам'яті так само, як стек.
  • У C ++ змінні на купі повинні руйнуватися вручну і ніколи не виходити з сфери застосування. Дані звільнені delete, delete[], або free.
  • Повільніше виділяти в порівнянні зі змінними на стекі.
  • Використовується за запитом для виділення блоку даних для використання програмою.
  • Може мати фрагментацію, коли існує велика кількість асигнувань та звільнень.
  • У C ++ або C, дані, створені на купі, будуть вказуватись на покажчики та розподілені за допомогою new або malloc відповідно.
  • Можливо, збій з розподілом, якщо надто великий буфер вимагається виділити.
  • Ви б використовували купу, якщо ви точно не знаєте, скільки даних вам знадобляться під час виконання, або якщо вам потрібно виділити багато даних.
  • Відповідальний за витік пам'яті.

Приклад:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

2095
2017-09-17 04:20



Покажчик pBuffer і значення b розташовані на стекі, і вони, найімовірніше, виділяються при вході в функцію. Залежно від компілятора, буфер може бути виділений також на вхід функції. - Andy
Це загальне помилкове уявлення про те, що C мова, як визначено в C99 мовний стандарт (доступний на open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), вимагає "стека". Справді, слово "стека" навіть не відображається в стандарті. Це відповідає заявам wrt / to CВикористання стеків в цілому є істинним, але це ніяк не вимагається від мови. Побачити knosof.co.uk/cbook/cbook.html для отримання додаткової інформації та, зокрема, способу C реалізовано на непарних архітектурах, таких як ru.wikipedia.org/wiki/Burroughs_large_systems - johne
@ Брайан Ви повинні пояснити чому буфер [] та покажчик pBuffer створюються на стекі і чому дані pBuffer створюються на купі. Я думаю, що деякий ppl може бути заплутаним вашою відповіддю, оскільки вони можуть вважати, що програма спеціально доручає виділити пам'ять на стек проти купи, але це не так. Це тому, що буфер є типом значення, а pBuffer - еталонним типом? - Howiecamp
@ Перекидання: відсутність покажчика містить адресу, і він може вказувати на щось на купі або в стекі однаково. new, malloc і деякі інші функції, подібні до malloc, виділяють на купу та повертають адресу пам'яті, яка була виділена. Чому б ви хотіли виділити на купу? Так що ваша пам'ять не вийде за межі сфери діяльності та звільняється, доки ви цього не хочете. - Brian R. Bondy
"Відповідає за витік пам'яті" - купи не несуть відповідальності за витік пам'яті! Lazy / Forgetful / ex-java кодерів / кодерів, які не дають лайно! - Laz


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

  • У наборі предметів елементи сидять один над одним у тому порядку, в якому вони були поміщені, і ви можете видалити тільки верхню (без скидання всього цього).

    Stack like a stack of papers

    Простота стека полягає в тому, що вам не потрібно зберігати таблицю, що містить запис кожного розділу виділеної пам'яті; єдина інформація про стан потрібна лише вказівник на кінець стек. Щоб виділити і де-атрибути, ви просто збільшувати і зменшувати цей окремий покажчик. Примітка. Стік іноді може бути реалізований для запуску у верхній частині розділу пам'яті і продовжується вниз, а не зростає вгору.

  • У купі немає особливого порядку для розміщення предметів. Ви можете охопити та видалити елементи у будь-якому порядку, оскільки немає чіткого "верхнього" елемента.

    Heap like a heap of licorice allsorts

    Розподіл купи вимагає ведення повного звіту про те, яка пам'ять виділяється, а що ні, а також деякі додаткові витрати для зменшення фрагментації, пошук суміжних сегментів пам'яті, достатньо великих, щоб відповідати потрібному розміру тощо. Пам'ять може бути вилучена в будь-який час, залишаючи вільне місце. Іноді розподільник пам'яті буде виконувати завдання з обслуговування, такі як дефрагментація пам'яті шляхом переміщення виділеної пам'яті навколо або збирання сміття - виявлення під час виконання, коли пам'ять більше не охоплена і не вилучається.

Ці зображення повинні робити досить добре описати два способи розподілу та звільнення пам'яті в стекі та купі. Юм!

  • Наскільки вони контролюються операційною системою або мовою середовища?

    Як уже згадувалося, купа та стек є загальними термінами і можуть бути реалізовані багатьма способами. Комп'ютерні програми, як правило, мають стеку, який називається a стек викликів який зберігає інформацію, що стосується поточної функції, такої як покажчик на будь-яку функцію, з якої він був викликаний, і будь-які локальні змінні. Оскільки функції викликають інші функції, а потім повертаються, стек зростає і стискається, щоб тримати інформацію від функцій далі вниз до стеку викликів. Програма насправді не має контролю над його виконанням; це визначається мовою програмування, ОС і навіть архітектурою системи.

    Купа - це загальний термін, який використовується для будь-якої пам'яті, яка виділяється динамічно та випадково; тобто не в порядку. Зазвичай пам'ять, як правило, виділяється операційною системою, за допомогою функцій виклику API викликів для виконання цього розподілу. Для керування динамічно виділеною пам'яттю, яка, як правило, обробляється ОС, є достатньо накладних витрат.

  • Яка їхня сфера?

    Стік викликів - це така концепція низького рівня, що вона не стосується "сфери" у сенсі програмування. Якщо ви розбираєте якийсь код, ви побачите посилання відносного покажчика на частину стеків, але настільки, наскільки це стосується мови вищого рівня, мова накладає власні правила обсягу. Одним з важливих аспектів стеків, однак, полягає в тому, що як тільки функція повертається, то щось локальне для цієї функції негайно звільняється з стека. Це працює так, як ви очікуєте, що це буде працювати з урахуванням того, як працюють ваші мови програмування. У купі теж важко визначити. Сфера - це те, що ОС виявляє, однак ваша мова програмування, ймовірно, додає свої правила про те, що в вашому додатку є "область". Архітектура процесора та операційна система використовують віртуальну адресацію, яку процесор переводить на фізичні адреси, є помилки сторінки тощо. Вони відстежують, які сторінки відносяться до тих програм. Вам ніколи не потрібно турбуватися про це, однак, тому що ви просто використовуєте будь-який метод, який використовує мова програмування для виділення та вільної пам'яті, і перевіряє наявність помилок (якщо з будь-якої причини не вдалося розподілити / розблокувати).

  • Що визначає розмір кожного з них?

    Знову ж таки, це залежить від мови, компілятора, операційної системи та архітектури. Стек, як правило, попередньо виділяється, оскільки за визначенням повинна бути суміжна пам'ять (більше про це в останньому абзаці). Мова-компілятор або ОС визначають його розмір. Ви не зберігаєте величезні шматки даних у стекі, тому він буде достатньо великим, щоб його ніколи не можна було повною мірою використовувати, за винятком випадків небажаної нескінченної рекурсії (отже, "переповнення стеків") або інших незвичайних програмних рішень.

    Купа - це загальний термін для всього, що можна динамічно розподілити. Залежно від того, яким чином ви дивитесь на це, воно постійно змінює розмір. У сучасних процесорах і операційних системах точний спосіб роботи в будь-якому випадку дуже абстрагований, тому вам, як правило, не потрібно турбуватися про те, як він працює в глибині, крім того (на тих мовах, де це дозволяє) ви не повинні використовувати пам'ять, яка ви ще не виділили або пам'ять, яку ви звільнили.

  • Що робить швидше?

    Стійка швидше, тому що вся вільна пам'ять завжди суміжна. Не потрібно зберігати список усіх сегментів вільної пам'яті, лише один вказівник на поточну вершину стеку. Компілятори зазвичай зберігають цей покажчик спеціальним, швидким реєструйся Для цієї мети. Більше того, подальші операції в стеку, як правило, зосереджені в дуже близьких областях пам'яті, що на дуже низькому рівні добре для оптимізації процесором кеш-пам'яті.


1261
2018-03-19 14:38



Девід Я не згоден з тим, що це хороший імідж, або що "виштовхнутий стек" - це хороший термін для ілюстрації концепції. Коли ви додаєте щось до стеку, інше вміст стека ні натиснуті, вони залишаються там, де вони є. - thomasrutter
Ця відповідь містить велику помилку. Статичні змінні не виділяються на стек. Подивись мою відповідь [link] stackoverflow.com/a/13326916/1763801 для з'ясування. ви прирівнюєте "автоматичні" змінні з "статичними" змінами, але вони не зовсім однакові - davec
Зокрема, ви кажете, що "статично виділені локальні змінні" виділяються на стек. Фактично вони виділяються в сегменті даних. Тільки автоматично виділені змінні (які включають більшість, але не всі локальні змінні, а також речі, такі як функції, передані за значенням, а не за посиланням) виділяються в стек. - davec
Я просто зрозумів, що ти правий - у С, статичне виділення це власна річ, а не термін для чогось іншого динамічний. Я відредагував мою відповідь, дякую. - thomasrutter
Це не тільки C. Java, Pascal, Python та багато інших, у всіх є поняття статичного та автоматичного порівняння з динамічним розміщенням. Говорячи про "статичне виділення", це означає те ж саме практично скрізь. У жодній мові не статичний розподіл означає "не динамічний". Ви хочете термін "автоматичний" розподіл за те, що ви описуєте (тобто речі в стекі). - davec


(Я перевів цю відповідь з іншого питання, що було більш-менш дурнем цього.)

Відповідь на ваше запитання є конкретною для виконання та може відрізнятись між компіляторами та процесорними архітектурами. Однак тут є спрощений пояснення.

  • Як стека, так і купи розташовані області пам'яті, що виділяються з базової операційної системи (часто віртуальна пам'ять, яка за потребою підключена до фізичної пам'яті).
  • У багатопотоковій середовищі кожен потік має свій власний повністю незалежний стек, але вони поділяться купою. Паралельний доступ повинен контролюватися на купі і неможливий на стек.

Купа

  • Купа містить пов'язаний список використаних і вільних блоків. Нові надходження на купу (по new або malloc) задовольняються шляхом створення відповідного блоку з одного з вільних блоків. Це вимагає оновлення списку блоків на купі. Це мета інформація про блоки на купі також зберігається на купі часто в невеликій площі прямо перед кожним блоком.
  • По мірі зростання купи нові блоки часто виділяються з нижчих адрес у бік вищих адрес. Таким чином, ви можете думати про купу, як купа блоків пам'яті, які ростуть у розмірі, оскільки пам'ять виділяється. Якщо куча замало для розподілу, розмір часто можна збільшити, придбавши більше пам'яті з базової операційної системи.
  • Розподіл і вилучення багатьох малих блоків може залишити купу в стані, де є багато малих вільних блоків, переміщених між використаними блоками. Запит про виділення великого блоку може вийти з ладу, оскільки жоден з вільних блоків не є достатньо великим, щоб задовольнити запит розподілу, навіть якщо загальний розмір вільних блоків може бути досить великим. Це називається купа фрагментація.
  • Коли вилучений блок, що примикає до вільного блоку, новий вільний блок може бути об'єднаний із сусіднім вільним блоком для створення більшого вільного блоку, що ефективно зменшує фрагментацію купи.

The heap

Стек

  • Стек часто працює в тісному тандемі з спеціальним реєстром на ЦП з ім'ям покажчик стіка. Спочатку покажчик стеку вказує на верхню частину стека (найвища адреса в стеку).
  • Процесор має спеціальні вказівки для штовхати значення на стек і спливаюче їх повернути зі стеків. Кожен тиснути зберігає значення в поточному розташуванні покажчика стека та зменшує покажчик стека. А. поп отримує значення, вказане курсором стека, а потім збільшує покажчик стека (не плутайте той факт, що додавши значення стеку зменшується кутовий покажчик і видалення значення збільшується це Пам'ятайте, що стек зростає донизу). Значення, які зберігаються та завантажуються, є значеннями регістрів ЦП.
  • Коли називається функція, процесор використовує спеціальні вказівки, які натискають поточний покажчик інструкцій, тобто адресу коду, що виконується в стеку. Процесор потім переходить до функції, встановивши параметр вказівка ​​інструкції на адресу функції, що викликається. Пізніше, коли функція повертається, статичний покажчик інструкцій з'являється зі стеків, а виконання виконується у коді відразу після виклику функції.
  • Коли вводиться функція, покажчик стека зменшується, щоб виділити більше місця на стек для локальних (автоматичних) змінних. Якщо функція має одну локальну 32-бітну змінну, чотири байти відкладаються в стек. Коли функція повертається, покажчик стека повертається назад, щоб звільнити виділену область.
  • Якщо функція має параметри, вони виводяться до стек перед викликом функції. Код у функції потім зможе переміщати стек з поточного покажчика стека, щоб знайти ці значення.
  • Функція гніздування викликає роботу, як чарівність. Кожен новий виклик виділить параметри функції, адресу повернення та простір для локальних змінних та цих записи активації може бути стековано для вкладеного дзвінка і буде розвеселити правильно, коли функції повернуться.
  • Оскільки стек є обмеженим блоком пам'яті, ви можете викликати a переповнення стеку викликаючи забагато вкладені функції та / або виділяючи забагато місця для локальних змінних. Часто область пам'яті, що використовується для стека, налаштовується таким чином, що написання нижче дна (найнижчої адреси) стеку призведе до появи лову чи виключення в ЦП. Цю виняткову ситуацію можна потрапити під час виконання і перетворити на певний випадок переповнення стека.

The stack

Чи може функція бути розподілена на купі замість стек?

Ні, записи активації для функцій (тобто локальних або автоматичних змінних) виділяються на стек, який використовується не тільки для зберігання цих змінних, але також для відстеження викликів вкладеної функції.

Наскільки керована куча - дійсно до середовища виконання. C використовує malloc і використовує C ++ new, але на багатьох інших мовах є збір сміття.

Проте стек є більш низькорівневим об'єктом, тісно пов'язаним із архітектурою процесора. Зростання купи, коли місця недостатньо, не так вже й важко, оскільки воно може бути реалізоване у бібліотечному дзвінку, який обробляє купу. Однак збільшення стека часто неможливе, оскільки переповнення стекту виявляється лише тоді, коли вже пізно; і вимикання потоку виконання є єдиним життєздатним варіантом.


664
2017-07-31 15:54



@Мартін - дуже хороша відповідь / пояснення, ніж більш абстрактна прийнята відповідь. Програма збірки зразків, що показує покажчики стеків / регістри, що використовуються для викликів функцій vis, більш ілюстративні. - Bikal Lem
Кожен тип посилання - це склад типів значень (int, string тощо). Як вже було сказано, ці типи значень зберігаються в стеку, ніж це працює, коли вони є частиною еталонного типу. - Nps
Ця відповідь була найкращою, на мій погляд, тому що це допомогло мені зрозуміти, що таке твердження про повернення, і як він пов'язаний з цією "поверненням адреси", які я зараз стикаюся, що це означає, щоб натиснути функцію на стек, і чому функції виштовхуються на стек. Відмінна відповідь! - Alex
Це найкраще, на мій погляд, саме для того, щоб згадати, що це купа / стек дуже реалізація специфічна. Інші відповіді приймають a багато речей про мову та навколишнє середовище / ОС. +1 - Qix
Що ви маєте на увазі "Код в цій функції, то зможе переміщати стек з поточного покажчика стеку, щоб знайти ці значення". ? Чи можете ви розповісти про це, будь ласка? - Koray Tugay


У наступному коді C #

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Ось як управляється пам'ять

Picture of variables on the stack

Local Variables що потрібно тривати до тих пір, поки виклик функції переходить у стек. Купа використовується для змінних, життя яких ми дійсно не знаємо фронт, але ми очікуємо, що вони триватимуть деякий час. У більшості мов критично, що під час компіляції ми знаємо, наскільки велика змінна, якщо ми хочемо зберегти її в стекі.

Об'єкти (які відрізняються за розмірами, коли ми оновлюємо їх), йдуть на купу, тому що ми не знаємо, на час створення, скільки часу вони будуть дотримуватися. На багатьох мовах куча збирають сміття для пошуку об'єктів (таких як об'єкт cls1), які більше не мають жодних посилань.

У Java більшість об'єктів прямують безпосередньо в купу. У таких мовах, як C / C ++, структури та класи часто залишаються в стекі, коли ви не маєте відношення до покажчиків.

Більше інформації можна знайти тут:

Різниця між розподілом пам'яті стеком та купою «timmurphy.org

і тут:

Створення об'єктів на стеку та купі

Ця стаття є джерелом зображення вище: Шість важливих концепцій .NET: Стек, купи, типи значень, типи рефератів, бокс та розблокування - CodeProject

але пам'ятайте, що це може містити деякі неточності.


352
2017-11-09 12:28



Це неправильно. I та cls - це не "статичні" змінні. вони називаються "локальними" або "автоматичними" змінами. Це дуже важлива відмінність. Див [link] stackoverflow.com/a/13326916/1763801 для з'ясування - davec
Я не сказав, що вони статичні змінні. Я сказав, що int і cls1 статичні предмети. Їх пам'ять статично виділяється, і тому вони йдуть у стек. Це на відміну від об'єкта, що вимагає динамічного розподілу пам'яті, який, отже, йде на купу. - Snowcrash
Я цитую "Статичні елементи ... переходьте до стек". Це просто погано. Статичні елементи надходять у сегмент даних, автоматичні елементи переходять у стек. - davec
Також кожен, хто написав цю статтю, не знає, про що він говорить. Наприклад, він каже, що "первісні потребують пам'яті статичного типу", що абсолютно не відповідає дійсності. Ніщо не перешкоджає вам динамічно розподіляти примітиви в купі, просто напишіть щось на зразок "int array [] = new int [num]" і вуалі, примітиви, що динамічно виділяються в .NET. Це лише одне з декількох неточностей. - davec
Я відредагував ваш пост, оскільки ви зробили серйозні технічні помилки щодо того, що відбувається в скупченні та купі. - Tom Leys


Стек Коли ви викликаєте функцію, аргументи до цієї функції плюс деякі інші накладні витрати ставляться до стеку. Також зберігається деяка інформація (наприклад, де можна повернутися). Коли ви оголошуєте змінну всередині вашої функції, ця змінна також виділяється на стек.

Видалення стека досить просто, тому що ви завжди вилучаєте в зворотному порядку, в якому ви виділяєте. Складові вміст додаються, коли ви вводите функції, відповідні дані видаляються при виході з них. Це означає, що ви, як правило, перебуваєте в невеликому регіоні стеків, якщо ви не називаєте багато функцій, які викликають безліч інших функцій (або створюєте рекурсивне рішення).

Купа Купа - загальна назва, за якою ви поміщаєте дані, які ви створюєте на льоту. Якщо ви не знаєте, скільки космічних кораблів ваша програма збирається створити, скоріше за все, ви зможете використовувати новий (або malloc або еквівалентний) оператор, щоб створити кожен космічний корабель. Цей розподіл буде тривати деякий час, тому, ймовірно, ми будемо позбавляти речі в іншому порядку, ніж ми їх створили.

Таким чином, купа набагато складніша, тому що в кінцевому підсумку це регіони пам'яті, які не використовуються між собою з фрагментами - пам'ять фрагментована. Пошук вільної пам'яті про розмір, який вам потрібен, є складною проблемою. Ось чому купу слід уникати (хоча вона як і раніше часто використовується).

Реалізація Реалізація як стека, так і купи звичайно переходить до runtime / OS. Часто ігри та інші додатки, які є критично важливими, створюють власні рішення для пам'яті, які захоплюють велику кількість пам'яті з купи, а потім виготовляють її з внутрішньої сторони, щоб не покладатися на ОС для пам'яті.

Це буде практично тільки в тому випадку, якщо використання вашої пам'яті суттєво відрізняється від норми, тобто для ігор, в яких ви завантажуєте рівень в одну величезну операцію, і можете скористатись усіма перевагами в іншій величезній операції.

Фізичне розташування в пам'яті Це менш релевантне, ніж ви думаєте, з-за називаної технології Віртуальна пам'ять що робить вашу програму думкою, що у вас є доступ до певної адреси, де фізичні дані знаходяться деінде (навіть на жорсткому диску!). Адреси, які ви отримуєте для стеків, у порядку збільшення, оскільки ваш дерево викликів стає глибшим. Адреси для купи є непередбачуваними (тобто імпліментація специфічна) і, прямо скажемо, не важливо.


191
2017-09-17 04:27



Рекомендація, щоб уникнути використання купи досить сильна. Сучасні системи мають хороших менеджерів, а сучасні динамічні мови широко використовують купу (без того, що програміст дійсно турбує про це). Я б сказав, скористайтеся купою, але з ручним розподілом, не забувайте вільно! - Greg Hewgill
Якщо ви можете використовувати стек або купу, скористайтеся стек. Якщо ви не можете використовувати стек, насправді немає вибору. Я використовую як багато, так і, звичайно, використовуючи std :: vector або подібні хіти в купу. Для новачка ви уникаєте купи, тому що стек просто так просто! - Tom Leys
Якщо вашою мовою не виконується збирання сміття, Смарт-вказівники (Частково виділені об'єкти, що обертаються навколо покажчика, який містить посилання для підрахунку динамічно виділених фрагментів пам'яті) тісно пов'язані з збиранням сміття та є пристойним способом управління кучкою в безпечному режимі і витік вільний спосіб. Вони реалізуються в різних рамках, але вони також не так важко реалізувати для ваших власних програм. - BenPen
"Ось чому купу слід уникати (хоча вона як і раніше часто використовується)". Я не впевнений, що це практично означає, особливо тому, що на багатьох мовах високого рівня пам'ять управляється по-різному. Оскільки це питання позначено мовно-агностичним, я б сказав, що цей конкретний коментар / рядок погано розміщено та не застосовується. - JonnoHampson
Хороша точка @JonnoHampson - Хоча ви робите правильну точку, я б стверджував, що якщо ви працюєте на "мові високого рівня" з GC, ви, напевно, не хвилюєтеся про механізми розподілу пам'яті взагалі - і так не робіть навіть дбайливо, що таке і купа. - Tom Leys


Щоб уточнити, ця відповідь має неправильну інформацію (Томас фіксував свою відповідь після коментарів, класний :)). Інші відповіді просто уникайте пояснення того, що означає статичне виділення. Тому я поясню три основні форми розподілу і як вони, як правило, відносяться до купи, стеку та сегменту даних нижче. Я також покажу кілька прикладів як в C / C ++, так і в Python, щоб допомогти людям зрозуміти.

Статичні (статично виділені AKA) змінні не виділяються на стек. Не вважайте так - багато людей роблять лише тому, що "статичний" звучить як "стек". Вони насправді не існують ні в стеку, ні в купі. Вони є частиною того, що називається сегмент даних.

Проте, як правило, краще розглянути "обсяг"і"час життя", а не" стек "і" куча ".

Сфера стосується того, які частини коду можуть мати доступ до змінної. Взагалі ми думаємо про це місцевий масштаб (можна отримати доступ лише до поточної функції) порівняно з глобальний масштаб (може бути доступний в будь-якому місці), хоча охоплення може стати набагато складнішим.

Тривалість життя означає, коли змінна виділяється і вилучається під час виконання програми. Зазвичай ми думаємо про це статичне виділення (змінна зберігатиметься протягом усієї тривалості програми, що робить його корисним для зберігання однієї інформації за кількома викликами функції) порівняно з автоматичне розподіл (змінна зберігається лише під час одного виклику функції, що робить його корисним для зберігання інформації, яка використовується лише під час вашої функції, і може бути відкинута після завершення), порівняно з динамічний розподіл (змінні, тривалість яких визначається під час виконання, а не час компіляції, як статичний або автоматичний).

Хоча більшість компіляторів і інтерпретаторів реалізують цю поведінку аналогічно з точки зору використання стеків, купи тощо, компілятор іноді може порушити ці конвенції, якщо він хоче, доки поведінка буде правильною. Наприклад, через оптимізацію локальна змінна може існувати лише в реєстрі або повністю вилучатися, навіть якщо більшість місцевих змінних існують у стекі. Як зазначалося в декількох коментарях, ви можете втілювати компілятор, який навіть не використовує стек або купу, а замість деяких інших механізмів зберігання даних (це рідко виконується, оскільки для цього є великі купи і скупчення).

Я надам деякий простий анотований код C, щоб проілюструвати все це. Найкращий спосіб навчитися - запустити програму під відладчиком та спостерігати за поведінкою. Якщо ви вважаєте за краще читати python, переходьте до кінця відповіді :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Особливо гострим прикладом того, чому важливо відрізняти термін служби та масштаби, є те, що змінна може мати місцевий обсяг, але статичний термін служби - наприклад, "someLocalStaticVariable" у прикладі коду вище. Такі змінні можуть зробити нашу спільну, але неформальну звичку називати дуже заплутаною. Наприклад, коли ми говоримо "місцевий"ми звичайно означаємо"локально визначена автоматично виділена змінна"і коли ми говоримо про глобальну, ми звичайно означаємо"глобально виділена статично виділена змінна"На жаль, коли справа доходить до таких речей, як"Обмежені статично виділені змінні"багато людей просто кажуть ..."ха?".

Деякі варіанти синтаксису в C / C ++ загострюють цю проблему - наприклад, багато людей думають, що глобальні змінні не є "статичними" через синтаксис, наведений нижче.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Зверніть увагу, що розміщення ключового слова "статичне" у зазначеній вище декларації перешкоджає var2 мати глобальний обсяг. Тим не менш, глобальний var1 має статичне виділення. Це не інтуїтивно! З цієї причини я намагаюся ніколи не використовувати слово "статичний" при описі сфер, а замість цього сказати щось на зразок "файлу" або "обмеженому файлом". Однак багато людей використовують фразу "статичний" або "статичний" для опису змінної, доступ до якої можна отримати лише з одного коду файлу. У контексті життя "статичний" завжди означає, що змінна виділяється під час запуску програми та вилучається, коли програма завершується.

Деякі люди вважають ці поняття специфічними для C / C ++. Вони не. Наприклад, наведений нижче зразок Python ілюструє всі три типи розподілу (існують деякі тонкі розбіжності на інтерпретованих мовах, які я не опиниться тут).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

169
2017-09-17 04:48



Я хотів би послатися на статичну змінну, оголошену в межах функції, що має лише локальну доступність, але зазвичай не використовуватиме термін "сфера". Також варто відзначити, що один аспект стек / купу, з якими мовами має практично нульову гнучкість: мова, яка зберігає контекст виконання у стекі, не може використовувати той самий стек для зберігання речей, які повинні перевищити контексти, в яких вони створені . Деякі мови схожі PostScript мають кілька стеків, але мають "купу", яка веде себе більше як стека. - supercat
@supercat Все це має сенс Я визначив сферу, як "яку частину коду можна доступ змінна "(і відчуваю, що це найбільш стандартне визначення), тому я думаю, що ми згодні :) - davec
Я б розглядав "сферу" a змінна як обмежений як часом, так і простором. Змінна для об'єкта класу-об'єкта потрібна для збереження її значення, якщо об'єкт існує. Змінна в межах контексту виконання контексту потрібна для збереження її значення, якщо виконання виконується в цьому контексті. Статична декларація змінної створює a ідентифікатор чий обсяг обмежений поточним блоком, який додається до a змінна сфера якої необмежена. - supercat
@supercat Саме тому я використовую слово lifetime, яким чином я називаю те, що ви називаєте тимчасовим масштабом. Це зменшує необхідність перевантаження слова "сфера" з таким численним значенням. Наскільки я можу сказати, здається, немає абсолютного консенсусу щодо точних визначень навіть у канонічних джерелах. Моя термінологія використовується частково з K & R та частково з переважного використання на першому відділенні CS, я вивчав / викладав у. Завжди добре чути інший інформований вигляд. - davec
ви напевно жартуєте. Чи можете ви дійсно визначити статичну змінну всередині функції? - Zaeem Sattar