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

1. Введение

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

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

2. Стековая память в Java

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

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

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

2.1. Ключевые особенности стековой памяти

Помимо того, что мы обсуждали до сих пор, ниже приведены некоторые другие особенности стековой памяти:

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

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

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

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

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

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

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

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

3.1. Ключевые особенности Java Heap Memory

Помимо того, что мы обсуждали до сих пор, ниже приведены некоторые другие особенности пространства кучи:

  • Доступ к нему осуществляется с помощью сложных методов управления памятью, включая молодое поколение, старое или постоянное поколение и постоянное поколение.
  • Если пространство кучи заполнено, 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. Upon entering the main() method, a space in stack memory would be created to store primitives and references of this method
    • The primitive value of integer id will be stored directly in stack memory
    • The reference variable person of type Person will also be created in stack memory which will point to the actual object in the heap
  2. The call to the parameterized constructor Person(int, String) from main() will allocate further memory on top of the previous stack. This will store:
    • The this object reference of the calling object in stack memory
    • The primitive value id in the stack memory
    • The reference variable of String argument name which will point to the actual string from string pool in heap memory
  3. The main method is further calling the buildPerson() static method, for which further allocation will take place in stack memory on top of the previous one. This will again store variables in the manner described above.
  4. However, for the newly created object person of type Person, all instance variables will be stored in heap memory.

This allocation is explained in this diagram:

5. Summary

Before we conclude this article, let's quickly summarize the differences between the Stack Memory and the Heap Space:

Parameter Stack Memory Heap Space
Application Stack is used in parts, one at a time during execution of a thread The entire application uses Heap space during runtime
Size Stack has size limits depending upon OS and is usually smaller then Heap There is no size limit on Heap
Storage Stores only primitive variables and references to objects that are created in Heap Space All the newly created objects are stored here
Order It is accessed using Last-in First-out (LIFO) memory allocation system This memory is accessed via complex memory management techniques that include Young Generation, Old or Tenured Generation, and Permanent Generation.
Life Stack memory only exists as long as the current method is running Heap space exists as long as the application runs
Efficiency Comparatively much faster to allocate when compared to heap Slower to allocate when compared to stack
Allocation/Deallocation Эта память автоматически выделяется и освобождается при вызове и возврате метода соответственно. Пространство кучи выделяется при создании новых объектов и освобождается с помощью Gargabe Collector, когда на них больше нет ссылок.

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

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

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