Рубрики
Без рубрики

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

Изучите, как работает память стека и пространство кучи и когда их следует использовать для разработки лучших программ Java.

Автор оригинала: baeldung.

1. введение

Чтобы запустить приложение оптимальным образом, JVM делит память на память стека и кучи. Всякий раз, когда мы объявляем новые переменные и объекты, вызываем новый метод, объявляем Строку или выполняем аналогичные операции, 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

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

  • Доступ к нему осуществляется с помощью сложных методов управления памятью, которые включают в себя Молодое поколение, Старое или Штатное поколение и Постоянное поколение
  • Если пространство кучи заполнено, 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() в памяти стека будет создано пространство для хранения примитивов и ссылок этого метода

    • Примитивное значение integer id будет храниться непосредственно в памяти стека
    • Ссылочная переменная person типа Person также будет создана в памяти стека, которая будет указывать на фактический объект в куче
  2. Вызов параметризованного конструктора Person(int, String) из main() выделит дополнительную память поверх предыдущего стека. Это будет хранить:

    • Ссылка this на объект вызывающего объекта в памяти стека
    • Примитивное значение id в памяти стека
    • Ссылочная переменная String аргумент имя , которая будет указывать на фактическую строку из пула строк в памяти кучи
  3. Метод main далее вызывает статический метод build Person () , для которого дальнейшее выделение будет происходить в памяти стека поверх предыдущего. Это снова сохранит переменные способом, описанным выше.
  4. Однако для вновь созданного объекта person типа Person все переменные экземпляра будут храниться в памяти кучи.

Это распределение объясняется на этой диаграмме: