Java Heap Space против Stack – Выделение памяти в Java

Когда-то ранее я написал несколько постов о сборке мусора в 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 и стековой памятью

На основе вышеизложенного можно легко вывести следующие различия между кучей и стековой памятью.

  1. Память кучи используется всеми частями приложения, тогда как стековая память используется только одним потоком выполнения.
  2. Когда объект создается, он всегда хранится в области Heap, а память стека содержит ссылку на него. Память стека содержит только локальные примитивные переменные и ссылочные переменные на объекты в области Heap.
  3. Объекты, хранящиеся в куче, глобально доступны, в то время как память стека не может быть доступна другим потокам.
  4. Управление памятью в стеке выполняется в порядке Last In First Out (LIFO), в то время как в памяти Heap это более сложно из-за глобального использования. Память Heap делится на Young-Generation, Old-Generation и т. д., подробности см. в Сборке мусора в Java.
  5. Память стека имеет короткий срок службы, в то время как память кучи живет с начала до конца выполнения приложения.
  6. Мы можем использовать параметры JVM -Xms и -Xmx для определения размера запуска и максимального размера памяти Heap. Мы можем использовать -Xss для определения размера памяти стека.
  7. Когда память стека полностью заполняется, Java runtime генерирует исключение java.lang.StackOverflowError, в то время как если память кучи заполняется, она генерирует ошибку java.lang.OutOfMemoryError: Java Heap Space.
  8. Размер памяти стека значительно меньше по сравнению с памятью Heap. Из-за простоты выделения памяти (LIFO) память стека очень быстра по сравнению с памятью Heap.

Это все для Сравнения памяти Java Heap Space и Stack Memory в контексте java-приложения, надеюсь, это разъяснило ваши сомнения относительно выделения памяти при выполнении любой программы на Java.

Ссылка: https://en.wikipedia.org/wiki/Java_memory_model.

Source:
https://www.digitalocean.com/community/tutorials/java-heap-space-vs-stack-memory