Перейти к основному содержимому

Память стека и пространство кучи в Java

· 6 мин. чтения

1. Введение

Для оптимального запуска приложения JVM делит память на стек и динамическую память. Всякий раз, когда мы объявляем новые переменные и объекты, вызываем новый метод, объявляем строку или выполняем аналогичные операции, JVM выделяет память для этих операций либо из памяти стека, либо из пространства кучи.

В этом уроке мы рассмотрим эти модели памяти. Во-первых, мы рассмотрим их ключевые особенности. Затем мы узнаем, как они хранятся в оперативной памяти и где их использовать. Наконец, мы обсудим ключевые различия между ними.

2. Память стека в Java

Память стека в Java используется для выделения статической памяти и выполнения потока. Он содержит примитивные значения, характерные для метода, и ссылки на объекты, на которые ссылается метод, находящиеся в куче.

Доступ к этой памяти осуществляется в порядке «последним поступил – первым обслужен» (LIFO). Всякий раз, когда мы вызываем новый метод, поверх стека создается новый блок, который содержит значения, характерные для этого метода, такие как примитивные переменные и ссылки на объекты.

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

2.1. Основные характеристики стековой памяти

Некоторые другие особенности стековой памяти включают в себя:

  • Он увеличивается и уменьшается по мере вызова и возврата новых методов соответственно.
  • Переменные внутри стека существуют только до тех пор, пока работает создавший их метод.
  • Он автоматически выделяется и освобождается, когда метод завершает выполнение.
  • Если эта память заполнена, Java выдает ошибку java.lang.StackOverFlowError.
  • Доступ к этой памяти быстрый по сравнению с динамической памятью.
  • Эта память является потокобезопасной, поскольку каждый поток работает в своем собственном стеке.

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

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

Эти объекты имеют глобальный доступ, и мы можем получить к ним доступ из любой точки приложения.

Мы можем разбить эту модель памяти на более мелкие части, называемые поколениями, а именно:

  1. Young Generation – здесь размещаются и стареют все новые объекты. Незначительная сборка мусора происходит, когда он заполняется.
  2. Старое или постоянное поколение — здесь хранятся долгоживущие объекты. Когда объекты хранятся в молодом поколении, устанавливается порог возраста объекта, и когда этот порог достигается, объект перемещается в старое поколение.
  3. Постоянная генерация — состоит из метаданных JVM для классов среды выполнения и методов приложения.

Эти различные части также обсуждаются в статье « Разница между JVM, JRE и JDK».

Мы всегда можем манипулировать размером кучи памяти в соответствии с нашими требованиями. Для получения дополнительной информации посетите эту связанную статью ForEach .

3.1. Ключевые особенности памяти кучи Java

Некоторые другие особенности пространства кучи включают в себя:

  • Доступ к нему осуществляется с помощью сложных методов управления памятью, которые включают в себя молодое поколение, старое или постоянное поколение и постоянное поколение.
  • Если место в куче заполнено, Java выдает ошибку java.lang.OutOfMemoryError.
  • Доступ к этой памяти сравнительно медленнее, чем к памяти стека.
  • Эта память, в отличие от стека, не освобождается автоматически. Ему нужен сборщик мусора, чтобы освободить неиспользуемые объекты, чтобы сохранить эффективность использования памяти.
  • В отличие от стека, куча не является потокобезопасной и должна быть защищена путем правильной синхронизации кода.

4. Пример

Основываясь на том, что мы уже узнали, давайте проанализируем простой код 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 , которая будет указывать на реальный объект в куче.
  1. Вызов параметризованного конструктора Person(int, String) из main() выделит дополнительную память поверх предыдущего стека. Это будет хранить:
  • Ссылка на этот объект вызывающего объекта в памяти стека
  • Идентификатор примитивного значения в памяти стека
  • Ссылочная переменная имени аргумента String , которая будет указывать на фактическую строку из пула строк в куче памяти.
  1. Основной метод — это дальнейший вызов статического метода buildPerson () , для которого дальнейшее выделение будет происходить в памяти стека поверх предыдущего. Это снова сохранит переменные в порядке, описанном выше.
  2. Однако в памяти кучи будут храниться все переменные экземпляра для вновь созданного объекта person типа Person.

Давайте посмотрим на это распределение на диаграмме ниже:

./ec8e88583c3aa1eec8cd226c68803b04.png

5. Резюме

Прежде чем мы завершим эту статью, давайте кратко суммируем различия между памятью стека и пространством кучи:

   | Параметр    | Память стека    | Куча пространства   | 
| Заявление | Стек используется частями, по одному во время выполнения потока | Все приложение использует пространство кучи во время выполнения. |
| Размер | Стек имеет ограничения по размеру в зависимости от ОС и обычно меньше, чем куча. | Для кучи нет ограничений по размеру |
| Хранилище | Хранит только примитивные переменные и ссылки на объекты, созданные в Heap Space. | Все вновь созданные объекты хранятся здесь |
| Заказ | Доступ к нему осуществляется с использованием системы распределения памяти «последним пришел — первым обслужен» (LIFO). | Доступ к этой памяти осуществляется с помощью сложных методов управления памятью, включая «молодое поколение», «старое или постоянное поколение» и «постоянное поколение». |
| Жизнь | Память стека существует только до тех пор, пока работает текущий метод. | Пространство кучи существует до тех пор, пока работает приложение |
| Эффективность | Гораздо быстрее выделить по сравнению с кучей | Медленнее выделять по сравнению со стеком |
| Распределение/освобождение | Эта память автоматически выделяется и освобождается при вызове и возврате метода соответственно. | Пространство кучи выделяется, когда новые объекты создаются и освобождаются Gargabe Collector, когда на них больше не ссылаются. |

6. Заключение

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

Чтобы узнать больше об управлении памятью в Java, ознакомьтесь с этой статьей здесь . Мы также затронули сборщик мусора JVM, который кратко обсуждается в этой статье .