Де зберігаються примітивні поля?
Примітивні поля зберігаються як частина об'єкта, який десь інстанціюється . Найпростіший спосіб подумати, де це - куча. Однак це не завжди так. Як описано в теорії та практиці Java: Переглянуті міські легенди про перегляд :
JVM можуть використовувати методику, що називається аналіз втечі, за допомогою якої вони можуть сказати, що певні об'єкти залишаються обмеженими однією ниткою протягом усього свого життя, і що час життя обмежений часом життя заданого кадру стека. Такі об'єкти можна сміливо виділяти на стеку замість купи. Ще краще, що для невеликих об'єктів JVM може повністю оптимізувати розподіл і просто підняти поля об’єкта в регістри.
Таким чином, не кажучи про те, що "об’єкт створений і поле теж є", не можна сказати, чи є щось на купі чи на стеці. Зауважте, що для невеликих короткотривалих об'єктів можливо, що "об'єкт" не буде існувати в пам'яті як такому, а натомість його поля можуть бути розміщені безпосередньо в регістрах.
У статті закінчується:
JVM на диво добре розбирати речі, про які ми припускали, що тільки розробник міг знати. Дозволяючи JVM вибирати між розподілом стеків і розподілом купи в кожному конкретному випадку, ми можемо отримати переваги від продуктивності розподілу стеків, не змушуючи програміста агонізувати, чи слід виділяти на стек чи на купу.
Таким чином, якщо у вас є код, який виглядає так:
void foo(int arg) {
Bar qux = new Bar(arg);
...
}
де ...
не дозволяє qux
залишити цю область, замість цього qux
може бути виділено на стеку. Це насправді виграш для VM, оскільки це означає, що йому не потрібно ніколи збирати сміття - воно зникне, коли він покине сферу застосування.
Детальніше про аналіз втечі у Вікіпедії. Для тих, хто бажає заглибитися в документи, аналіз втечі для Java від IBM. Для тих, хто походить із світу C #, ви можете знайти детальну інформацію про реалізацію та правду про типи цін Еріка Ліпперта (вони корисні також для типів Java, оскільки багато понять та аспектів однакові чи схожі) . Чому .Net книги говорять про розподіл стека проти купи пам'яті? також переходить до цього.
На віші стека і купи
На купу
Отже, навіщо взагалі стопку або купу? Для речей, які залишають сферу застосування, стек може бути дорогим. Розглянемо код:
void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
Параметри також є частиною стеку. У ситуації, коли у вас немає купи, ви передаваєте повний набір значень на стек. Це добре для "foo"
невеликих рядків ... але що трапиться, якщо хтось помістить величезний XML-файл у цей рядок. Кожен дзвінок копіював би всю стежку на стек - і це було б досить марно.
Натомість, краще розмістити об'єкти, які мають деяке життя поза межами безпосереднього простору (передані іншому простору, застрягли в структурі, яку підтримує хтось інший тощо), в іншу область, яку називають купою.
На стеку
Вам не потрібен стек. Можна, гіпотетично, написати мовою, яка не використовує стек (довільної глибини). Старий БАЗОВ, на якому я навчився в молодості, так і зробив, можна було робити лише 8 рівнів gosub
викликів, і всі змінні були глобальними - не було стека.
Перевага стека полягає в тому, що коли у вас є змінна, що існує з областю, коли ви залишаєте це поле, цей кадр стека вискакує. Це дійсно спрощує те, що там, а що ні. Програма переходить до іншої процедури, нового кадру стека; програма повертається до процедури, а ви знову в тій, яка бачить ваш поточний обсяг; програма залишає процедуру, і всі елементи в стеку розміщуються.
Це дійсно полегшує життя людині, яка пише час виконання для коду, щоб використовувати стек і купу. Вони просто багато понять і способів роботи над кодом, що дозволяє людині, яка пише код мовою, звільнитися від думки про них.
Природа стека також означає, що він не може бути фрагментованим. Фрагментація пам’яті є справжньою проблемою в купі. Ви виділяєте кілька об’єктів, потім сміття збираєте середній, а потім намагаєтесь знайти місце для наступного великого, який буде виділено. Його безлад. Бути в змозі помістити речі на стек означає, що вам не доведеться з цим боротися.
Коли щось збирають сміття
Коли щось зібрано сміття, його пішло. Але це лише зібране сміття, оскільки про нього вже забули - більше немає посилань на об’єкт у програмі, до яких можна отримати доступ із поточного стану програми.
Зазначу, що це дуже велике спрощення вивезення сміття. Є багато збирачів сміття (навіть у межах Java - ви можете налаштувати сміттєзбірник, використовуючи різні прапори ( документи ). Вони поводяться по-різному, і нюанси того, як кожен робить щось, є занадто глибокими для цієї відповіді. Ви можете прочитати Основи збору сміття Java, щоб краще зрозуміти, як працює щось із цього.
Однак, якщо щось виділено на стек, воно не є сміттям, яке збирається частиною System.gc()
- воно розміщується, коли кадр стека спливає. Якщо щось знаходиться на купі і посилається на щось, що стоїть на стеці, воно не буде збирати сміття на той час.
Чому це має значення?
Здебільшого, його традиція. Написані підручники та класи-компілятори та різноманітна документація по бітах значною мірою стосуються купи та стеку.
Однак сьогодні віртуальні машини (JVM та подібні) намагаються зберегти це приховано від програміста. Якщо у вас не вистачає одного чи іншого, і вам не потрібно знати, чому (а не просто належним чином збільшувати місце для зберігання), це не має великого значення.
Об'єкт знаходиться десь і знаходиться в тому місці, де до нього можна отримати правильний та швидкий доступ протягом відповідного часу, який він існує. Якщо його на стеці чи купі - це насправді не має значення.