Керування пам'яттю в Java
⚡ Розумний підсумок
Керування пам'яттю в Java пояснює, як JVM розподіляє пам'ять середовища виконання між стеком, купою, Code, і статичні регіони, як посилання на об'єкти обробляються під час викликів методів, і як збирач сміття повертає недоступні об'єкти, щоб програми залишалися стабільними та без витоків пам'яті.

Що таке стекова пам'ять Java?
Стекована пам'ять у Java — це область пам'яті JVM, яка зберігає фрейми методів, локальні змінні та змінні-посилання для кожного потоку. Доступ до стеку завжди здійснюється в порядку «Останній прийшов — Перший вийшов», тому метод, який був викликаний останнім, знаходиться зверху, а його локальні змінні завантажуються та витягуються разом з фреймом.
Кожна нитка в Java Віртуальна машина отримує власний стек, який забезпечує ізоляцію викликів методів та їх потокобезпечність за своєю природою. Примітивні локальні значення, такі як int, boolean та double, знаходяться безпосередньо всередині фрейму, тоді як посилання на об'єкти, що зберігаються в стеку, вказують на об'єкти, розміщені в купі.
Що таке купна пам'ять? Java?
Купа пам'яті — це спільна область JVM, яка містить усі Java об'єкт і масив, створені за допомогою оператора new, разом з будь-якими змінними посилання, що належать до цих об'єктів як поля екземпляра. На відміну від стеку (Stack), купа (Heap) спільна для всіх потоків, тому доступ до об'єктів часто вимагає синхронізації.
Купа (Heap) – це область, якою керує збирач сміття. Сучасні JVM HotSpot поділяють купу на молоде покоління для короткоживучих об'єктів та старе покоління для довгоживучих об'єктів, причому метадані класу зберігаються в окремому рідному регіоні під назвою Метапростір (Metaspace).
Розподіл пам'яті в Java
Розподіл пам'яті в Java — це процес, за допомогою якого JVM виділяє області віртуальної пам'яті для змінних та екземплярів класів і структур під час виконання програми. Пам'ять не виділяється об'єкту під час оголошення; створюється лише посилання. Фактичне виділення об'єктів відбувається за допомогою оператора new, тому кожен об'єкт знаходиться в купі (Heap).
Команда Java Розподіл пам'яті поділяється на такі розділи:
- купа
- Стек
- Code
- Статичний
Такий розподіл пам'яті необхідний для ефективного управління середовищем виконання.
- Команда Code розділ містить ваш скомпільований байт-код.
- Команда Стек секційні магазини методи, локальні змінні та змінні посилання.
- Команда купа розділ містить об'єкти а також може містити посилальні змінні, що зберігаються як поля екземпляра.
- Команда Статичний розділ тримає статичні дані та статичні методи спільний для всіх екземплярів.
Різниця між локальною та змінною екземпляра
Розуміння того, де кожен тип змінної знаходиться в JVM, допомагає пояснити поведінку стека та купи. змінна екземпляра оголошено всередині класу, але поза будь-яким методом, і він знаходиться в купі як частина свого об'єкта.
class Student{ int num; // num is instance variable public void showData{}
A локальна змінна оголошено всередині методу, включаючи аргументи методу, і знаходиться на стеку всередині активного фрейму.
public void sum(int a){ int x = a + 3; // a, x are local variables }
Різниця між стеком і купою
Стек і купа вирішують різні проблеми всередині JVM. Стек надає кожному потоку швидке, детерміноване виділення для короткоживучих даних, пов'язаних з областю видимості методу, тоді як купа надає спільну область для довгоживучих об'єктів, на які може посилатися будь-який потік. Коротке відео нижче підсумовує порівняння перед наступним покроковим оглядом.
Натисніть тут якщо відео недоступне
Щоб побачити, як взаємодіють Stack та Heap, розглянемо головний метод, який викликає метод m1.
public void m1{ int x = 20; }
У стеку JVM створюється фрейм для методу m1.
Змінна x у m1 також створюється у фреймі для m1 на стеку, як показано на зображенні нижче.
Метод m1 потім викликає метод m2. У стеку створюється новий кадр для m2 поверх кадру для m1.
Локальні змінні b та c також створюються всередині фрейму для m2 на стеку.
public void m2(int b){ boolean c; }
Далі, m2 викликає метод m3. Знову ж таки, фрейм для m3 створюється на вершині стека, як показано нижче.
Тепер припустимо, що метод m3 створює об'єкт для класу Account, який має два екземпляри змінних int p і int q.
class Account { int p; int q; }
Ось код методу m3.
public void m3(){ Account ref = new Account(); // more code }
Оператор new Account() створює об'єкт Account у купі.
Змінна посилання ref створюється на стеку всередині фрейму для m3.
Оператор присвоєння робить так, щоб змінна-посилання вказувала на об'єкт у купі.
Після завершення виконання методу керування повертається до викликаючого методу, яким у цьому випадку є метод m2.
Кадр для методу m3 видаляється зі стеку.
Оскільки змінна-посилання більше не вказує на об'єкт у купі, цей об'єкт стає придатним для збирання сміття.
Після завершення роботи методу m2 він видаляється зі стеку, і всі його змінні стають недоступними. Те саме відбувається з методом m1, і зрештою керування повертається до основного методу.
Що робити, якщо об'єкт містить інше посилання як свою змінну екземпляра?
public static void main(String args[]) { A parent = new A(); // more code } class A { B child = new B(); int e; } class B { int c; int d; }
У цьому випадку змінна посилання child знаходиться в купі як частина об'єкта A та, у свою чергу, вказує на власний об'єкт B, як показано нижче.
У чому полягає збір сміття Java?
Вивіз сміття в Java — це процес, за допомогою якого JVM автоматично керує пам'яттю. Збирач сміття знаходить об'єкти, які більше недоступні з жодного активного посилання, та повертає їхню пам'ять. Динамічне виділення пам'яті відбувається за допомогою оператора new, і пам'ять залишається виділеною, доки програма більше не містить жодного посилання на об'єкт.
Коли не залишається посилань, об'єкт вважається непотрібним, а пам'ять, яку він займає, можна звільнити. Немає явної потреби знищувати об'єкт, оскільки Java автоматично обробляє звільнення пам'яті через збирач сміття.
Техніка, що лежить в основі цього, відома як Сміттєвий збірПрограми, яким не вдається звільнити пам'ять, зрештою аварійно завершують роботу, коли більше нічого не залишається для розподілу. Кажуть, що такі програми мають витоку пам'яті. Вивіз сміття в Java працює автоматично протягом усього життєвого циклу програми, що усуває тягар ручного звільнення ресурсів та зменшує ризик витоків.
У мові C, навпаки, програміст відповідає за звільнення пам'яті, виділеної динамічно через функцію free(). Саме тут Java Управління пам'яттю пропонує значну перевагу.
Сучасні JVM HotSpot постачаються з кількома збирачами сміття, налаштованими на різні робочі навантаження. Збирач сміття G1 за замовчуванням орієнтований на збалансовану пропускну здатність та час пауз на купах об'ємом кілька гігабайт. ZGC та Shenandoah прагнуть пауз тривалістю менше мілісекунди на дуже великих купах, що робить їх...tracтивний для сервісів, чутливих до затримки. Вибір правильного колектора та налаштування розмірів молодого покоління, старого покоління та метапростору є ключовою навичкою для Java перформанс-робота.
Примітка: Усі об'єкти створюються в розділі купи пам'яті, який є областю, якою керує збирач сміття.
Приклад: вивчити механізм збору сміття в Java
У цьому покроковому посібнику показано, коли посилання стають придатними для збирання сміття всередині простої програми.
Крок 1) Скопіюйте наступний код у редактор.
class Student{ int a; int b; public void setData(int c,int d){ a=c; b=d; } public void showData(){ System.out.println("Value of a = "+a); System.out.println("Value of b = "+b); } public static void main(String args[]){ Student s1 = new Student(); Student s2 = new Student(); s1.setData(1,2); s2.setData(3,4); s1.showData(); s2.showData(); //Student s3; //s3=s2; //s3.showData(); //s2=null; //s3.showData(); //s3=null; //s3.showData(); } }
Крок 2) Збережіть, скомпілюйте та запустіть код. Як показано на діаграмі, створено два об'єкти та дві змінні-посилання.
Крок 3) Розкоментуйте рядки 20, 21 та 22. Збережіть, скомпілюйте та запустіть код.
Крок 4) Як показано на діаграмі нижче, дві змінні-посилання тепер вказують на один і той самий об'єкт.
Крок 5) Розкоментуйте рядки 23 та 24. Збережіть, скомпілюйте та запустіть код.
Крок 6) Як показано нижче, s2 стає null, але s3 все ще вказує на об'єкт, тому об'єкт ще не має права на збирання сміття.
Крок 7) Розкоментуйте рядки 25 та 26. Збережіть, скомпілюйте та запустіть код.
Крок 8) На цьому етапі жодні посилання не вказують на об'єкт, тому він стає придатним для збирання сміття. Збирач сміття видаляє його з пам'яті, і немає можливості його отримати.
Як видалити об'єкт у Java?
Java не надає оператора ручного видалення, тому стандартний підхід полягає у видаленні кожного посилання на об'єкт, щоб збирач сміття міг його повернути.
1) Щоб зробити об'єкт придатним для збирання сміття, присвоїте кожній змінній посилання, яка вказує на нього, значення null.
2) Примітивні типи не є об'єктами, тому їм не можна присвоїти значення null. Їхнє сховище автоматично звільняється, коли навколишній кадр Stack виштовхується.


















