Когда-то ранее я написал несколько постов о сборке мусора в Java и том, что Java передаётся по значению. После этого я получил много электронных писем с просьбой объяснить, что такое Java Heap Space, Java Stack Memory, Memory Allocation in Java и каковы их различия. Вы увидите много ссылок на память кучи и стека в книгах и учебниках по Java и Java EE, но лишь малая часть из них содержит полное объяснение того, что такое куча и стек памяти в терминах программы.
Java Heap Space
Пространство кучи Java используется Java Runtime для выделения памяти под объекты и классы JRE. Когда мы создаем объект, он всегда создается в пространстве кучи. Сборка мусора выполняется в пространстве кучи для освобождения памяти, используемой объектами, на которые нет ссылок. Любой объект, созданный в пространстве кучи, имеет глобальный доступ и может быть ссылкой откуда угодно в приложении.
Java Stack Memory
Java Stack memory используется для выполнения потока. В ней содержатся значения, специфичные для метода, которые являются кратковременными, а также ссылки на другие объекты в куче, на которые ссылается метод. Память стека всегда используется в порядке LIFO (последний вошел – первый вышел). Когда вызывается метод, в памяти стека создается новый блок для метода, чтобы содержать локальные примитивные значения и ссылки на другие объекты в методе. Как только метод завершается, блок становится неиспользуемым и становится доступным для следующего метода. Размер памяти стека очень меньше по сравнению с памятью кучи.
Память кучи и стека в программе Java
Давайте разберем использование памяти кучи и стека на примере простой программы.
package com.journaldev.test;
public class Memory {
public static void main(String[] args) { // Line 1
int i=1; // Line 2
Object obj = new Object(); // Line 3
Memory mem = new Memory(); // Line 4
mem.foo(obj); // Line 5
} // Line 9
private void foo(Object param) { // Line 6
String str = param.toString(); //// Line 7
System.out.println(str);
} // Line 8
}
На изображении ниже показаны память стека и кучи относительно указанной программы и как они используются для хранения примитивных значений, объектов и переменных ссылки. Давайте пройдемся по шагам выполнения программы.
- Как только мы запускаем программу, она загружает все классы времени выполнения в пространство кучи. Когда метод main() найден на строке 1, Java Runtime создает память стека для использования потоком main().
- Мы создаем примитивную локальную переменную в строке 2, поэтому она создается и хранится в стековой памяти метода main().
- Поскольку мы создаем объект в третьей строке, он создается в куче, а стековая память содержит ссылку на него. Подобный процесс происходит, когда мы создаем объект Memory в четвертой строке.
- Теперь, когда мы вызываем метод foo() в пятой строке, для использования методом foo() создается блок в верхней части стека. Поскольку Java передает значения по значению, в шестой строке в стековом блоке foo() создается новая ссылка на объект.
- A string is created in the 7th line, it goes in the String Pool in the heap space and a reference is created in the foo() stack space for it.
- Метод foo() завершается в восьмой строке, в этот момент выделенный для foo() блок памяти в стеке становится свободным.
- На девятой строке метод main() завершается, и стековая память, созданная для метода main(), уничтожается. Кроме того, программа завершается на этой строке, поэтому среда выполнения Java освобождает всю память и завершает выполнение программы.
Разница между кучей Java и стековой памятью
На основе вышеизложенного можно легко вывести следующие различия между кучей и стековой памятью.
- Память кучи используется всеми частями приложения, тогда как стековая память используется только одним потоком выполнения.
- Когда объект создается, он всегда хранится в области Heap, а память стека содержит ссылку на него. Память стека содержит только локальные примитивные переменные и ссылочные переменные на объекты в области Heap.
- Объекты, хранящиеся в куче, глобально доступны, в то время как память стека не может быть доступна другим потокам.
- Управление памятью в стеке выполняется в порядке Last In First Out (LIFO), в то время как в памяти Heap это более сложно из-за глобального использования. Память Heap делится на Young-Generation, Old-Generation и т. д., подробности см. в Сборке мусора в Java.
- Память стека имеет короткий срок службы, в то время как память кучи живет с начала до конца выполнения приложения.
- Мы можем использовать параметры JVM -Xms и -Xmx для определения размера запуска и максимального размера памяти Heap. Мы можем использовать -Xss для определения размера памяти стека.
- Когда память стека полностью заполняется, Java runtime генерирует исключение
java.lang.StackOverflowError
, в то время как если память кучи заполняется, она генерирует ошибкуjava.lang.OutOfMemoryError: Java Heap Space
. - Размер памяти стека значительно меньше по сравнению с памятью Heap. Из-за простоты выделения памяти (LIFO) память стека очень быстра по сравнению с памятью Heap.
Это все для Сравнения памяти Java Heap Space и Stack Memory в контексте java-приложения, надеюсь, это разъяснило ваши сомнения относительно выделения памяти при выполнении любой программы на Java.
Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory