Stack (Стек)
- Стекова пам'ять у Java використовується для виділення статичної пам'яті й виконання потоків.
Стек містить примітивні локальні значення методів й посилання на об'єкти, що знаходяться в купі.
- Доступ до пам'яті здійснюється в порядку LIFO (Last-In-First-Out). Коли ми викликаємо новий метод, новий блок створюється поверх стеку, що містить значення, що характерні для цього методу, такі як примітивні змінні й посилання на об'єкти.
- Коли метод завершує виконання, відповідний stack frame (стековий кадр - механізм передавання аргументів і виділення тимчасової пам'яті) скидається, потік повертається до методу, що викликається і звільняється місце для наступного методу.
Ключові особливості стекової пам'яті (стеку):
- Стек збільшується й зменшується по мірі виклику й повернення нових методів відповідно;
- Змінні всередині стеку існують лише до тих пір, поки працює метод, що їх створив;
- Стек автоматично виділяється й звільняється коли метод завершує виконання;
- Якщо стекова пам'ять заповнена, Java видає помилку java.lang.StackOverFlowError;
- Доступ до стекової пам'яті швидший, порівняно з купою (heap).
- Стекова пам'ять є потокобезпечною, оскільки кожен потік працює в своєму окремому стекові (під кожен потік виділяється окремий стек).
Heap (Купа)
- Купа (heap) використовується для динамічного виділення пам'яті для об'єктів Java і класів JRE під час виконання програми. Нові об'єкти завжди створюються в купі (heap), а посилання на ці об'єкти містяться в стеці (stack).
- Ці об'єкти Java мають глобальний доступ і ми можемо отримати до них доступ з будь-якої точки нашого додатку.
- Ми можемо розбити цю модель пам'яті на менші частини, які ми умовно називатимемо "поколіннями":
- Young Generation - тут містяться і старіють усі нові об'єкти. Незначна збірка сміття відбувається, коли він заповнюється.
- Old or Tenured Generation - тут зберігаються об'єкти-довгожителі. Коли об'єкти зберігаються в молодому поколінні, встановлюється поріг віку об'єкту й коли цей поріг досягається, об'єкт переміщується до старого покоління.
- Permanent Generation - складається з метаданих JVM для класів середовища виконання й методів додатку.
- Ми завжди можемо маніпулювати розміром пам'яті купи у відповідності з нашими вимогами.
Ключові особливості пам'яті купи (heap):
- Доступ до купи здійснюється за допомогою складних методі керування пам'яттю, що містять у собі "Молоде Покоління", "Старе Покоління", "Постійне Покоління".
- Якщо місце в купі заповнене, Java видає помилку java.lang.OutOfMemoryError.
- Доступ до цієї пам'яті повільніший, порівняно з доступом до пам'яті стеку.
- Пам'ять купи (heap) не звільняється автоматично, на відміну від стеку. Купі потрібен збирач сміття аби звільняти об'єкти, що не використовуються, задля ефективності використовування пам'яті.
- На відміну від стеку, купа не є потокобезпечною й повинна бути захищеною шляхом правильної синхронізації коду.
Приклад
Для прикладу проаналізуємо фрагмент Java-коду аби оцінити як тут можна керувати пам'яттю:
class Person {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public class PersonBuilder {
private static Person buildPerson(int id, String name) {
return new Person(id, name);
}
public static void main(String[] args) {
int id = 23;
String name = "John";
Person person = null;
person = buildPerson(id, name);
}
}
Проаналізуємо код крок за кроком:
1. Коли ми входимо до методу main(), у стекові створюється простір для зберігання примітивних типів даних і посилання цього методу.
- Пам'ять стеку напряму зберігає примітивне значення цілочисельного ідентифікатора.
- Також у пам'яті стеку буде створена посиланнєва змінна person типу (класу) Person, яка вказуватиме на реальний об'єкт у купі (heap).
2. Виклик параметризованого конструктора Person(int, String) з main() виділить додаткову пам'ять поверх попереднього стеку. Це міститиме:
- посилання на об'єкт this, що викликає об'єкт у пам'яті стеку.
- значення примітивного типу id в пам'яті стеку.
- посиланнєва змінна типу String, яка називається name, котра вказуватиме на фактичний рядок з пулу рядків у купі (heap).
3. Основний метод - подальший виклик статичного методу buildPerson(), для якого подальше виділення пам'яті відбуватиметься в пам'яті стеку поверх попереднього. Це знову збереже змінні в порядку, що описаний вище.
4. Однак у пам'яті купи зберігатимуться всі змінні екземпляру для знову створеного об'єкта person типу Person.
Comments