Поиск по сайту:

Пространство кучи Java против стека — выделение памяти в Java


Некоторое время назад я написал пару постов о том, что Java — это передача по значению. После этого я получил много писем с разъяснениями о пространстве кучи Java, памяти стека Java, распределении памяти в Java и в чем различия между ними. Вы увидите много ссылок на память кучи и стека в книгах и руководствах по Java, Java EE, но едва ли сможете найти полное объяснение того, что такое память кучи и стека с точки зрения программы.

Пространство кучи Java

Пространство кучи Java используется средой выполнения Java для выделения памяти для объектов и классов JRE. Всякий раз, когда мы создаем объект, он всегда создается в пространстве кучи. Сборка мусора выполняется в памяти кучи, чтобы освободить память, используемую объектами, на которые нет ссылок. Любой объект, созданный в куче, имеет глобальный доступ, и на него можно ссылаться из любого места приложения.

Память стека Java

Память стека Java используется для выполнения потока. Они содержат краткосрочные значения для конкретного метода и ссылки на другие объекты в куче, на которые ссылается метод. Память стека всегда используется в порядке 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 создает стековую память, которая будет использоваться потоком метода main().
  • Мы создаем примитивную локальную переменную в строке 2, поэтому она создается и сохраняется в памяти стека метода main().
  • Поскольку мы создаем объект в 3-й строке, он создается в куче памяти, а стековая память содержит ссылку на него. Аналогичный процесс происходит, когда мы создаем объект Memory в 4-й строке.
  • Теперь, когда мы вызываем метод foo() в 5-й строке, создается блок в верхней части стека, который будет использоваться методом foo(). Поскольку в Java используется передача по значению, новая ссылка на Object создается в блоке стека foo() в 6-й строке.
  • Строка создается в 7-й строке, она помещается в пул строк в пространстве кучи, и для нее создается ссылка в пространстве стека foo().
  • Метод foo() завершается в 8-й строке, в это время освобождается блок памяти, выделенный для foo() в стеке.
  • В строке 9 метод main() завершается, и память стека, созданная для метода main(), уничтожается. Кроме того, программа заканчивается на этой строке, поэтому среда выполнения Java освобождает всю память и завершает выполнение программы.

Разница между пространством кучи Java и памятью стека

Основываясь на приведенных выше объяснениях, мы можем легко сделать вывод о следующих различиях между памятью Heap и Stack.

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

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

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