Что такое heap и stack память в java
Перейти к содержимому

Что такое heap и stack память в java

  • автор:

Ошибки, которые возникают в JVM памяти

Али Жагпаров

Ошибки, которые возникают в JVM памяти

В этой статье мы разберем ошибки, которые могут возникать в JVM памяти.

Память JVM состоит из Heap и Stack областей.

Heap memory — область памяти, в которой хранятся Java объекты.

Stack memory — временная область памяти, в которой хранятся примитивные переменные и ссылки на объекты метода.

java.lang.OutOfMemoryError

Когда в JVM возникают проблемы с HEAP памятью, возникает ошибка java.lang.OutOfMemoryError. Кроме того, ошибка cодержит сообщение, которое поможет нам лучше понять проблему.

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Java heap space

Ошибка произошла при попытке создания объекта, JVM не смогла выделить область памяти для нового объекта.

GC Overhead limit exceeded

Сборщик мусора в JVM тратит слишком много времени на сборку мусора. Ошибка возникает, когда сборщик мусора тратит 98% времени на сборку мусора, но при этом освобождается менее 2% процентов памяти.

public class OutOfMemoryErrorDemo < public static void main(String[] args) < int i = 0; ListstringList = new ArrayList<>(); while (i < Integer.MAX_VALUE) < i++; String generatedString = new String( "some string generate to show out of memory error demo " + i); stringList.add(generatedString); >> >

stringList содержит ссылки на строки, которые мы создаем, и именно поэтому сборщик мусора не может удалить их. Это приводит к тому, что сборщик мусора тратит много времени на поиск объектов для удаления.

Для воспроизведения этой ошибки ограничьте память JVM вручную при помощи параметров -Xms и -Xmx

Requested array size exceeds VM limit

Возникает, когда мы пытаемся создать массив, который требует больше памяти, чем есть в HEAP.

public class OutOfMemoryErrorDemo < public static void main(String[] args) < // we try to create too long array long[] array = new long[Integer.MAX_VALUE]; >

Metaspace

Возникает, когда JVM не может выделить память для хранения информации о классах.

request size bytes for reason. Out of swap space?

Возникает, когда в native heap не хватает памяти или она близка к концу.

reason stack_trace_with_native_method

Эта ошибка возникает, когда JVM не удалось выделить память для JNI или нативных методов в heap.

StackOverFlowError

Появляется, когда стэк JVM переполнен. Чаще всего возникает из за бесконечной рекурсии.

public class StackOverFlowErrorDemo < public static void main(String[] args) < recursiveMethod(2); >public static int recursiveMethod(int i) < // it will never stop and it allocates all stack memory return recursiveMethod(i); >>

Основные принципы программирования: стек и куча

Обложка поста Основные принципы программирования: стек и куча

Мы используем всё более продвинутые языки программирования, которые позволяют нам писать меньше кода и получать отличные результаты. За это приходится платить. Поскольку мы всё реже занимаемся низкоуровневыми вещами, нормальным становится то, что многие из нас не вполне понимают, что такое стек и куча, как на самом деле происходит компиляция, в чём разница между статической и динамической типизацией, и т.д. Я не говорю, что все программисты не знают об этих понятиях — я лишь считаю, что порой стоит возвращаться к таким олдскульным вещам.

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

Стек

Стек — это область оперативной памяти, которая создаётся для каждого потока. Он работает в порядке LIFO (Last In, First Out), то есть последний добавленный в стек кусок памяти будет первым в очереди на вывод из стека. Каждый раз, когда функция объявляет новую переменную, она добавляется в стек, а когда эта переменная пропадает из области видимости (например, когда функция заканчивается), она автоматически удаляется из стека. Когда стековая переменная освобождается, эта область памяти становится доступной для других стековых переменных.

От пэт-проектов до наставничества: как джуну стать «качественным» мидлом

Из-за такой природы стека управление памятью оказывается весьма логичным и простым для выполнения на ЦП; это приводит к высокой скорости, в особенности потому, что время цикла обновления байта стека очень мало, т.е. этот байт скорее всего привязан к кэшу процессора. Тем не менее, у такой строгой формы управления есть и недостатки. Размер стека — это фиксированная величина, и превышение лимита выделенной на стеке памяти приведёт к переполнению стека. Размер задаётся при создании потока, и у каждой переменной есть максимальный размер, зависящий от типа данных. Это позволяет ограничивать размер некоторых переменных (например, целочисленных), и вынуждает заранее объявлять размер более сложных типов данных (например, массивов), поскольку стек не позволит им изменить его. Кроме того, переменные, расположенные на стеке, всегда являются локальными.

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

Куча

Куча — это хранилище памяти, также расположенное в ОЗУ, которое допускает динамическое выделение памяти и не работает по принципу стека: это просто склад для ваших переменных. Когда вы выделяете в куче участок памяти для хранения переменной, к ней можно обратиться не только в потоке, но и во всем приложении. Именно так определяются глобальные переменные. По завершении приложения все выделенные участки памяти освобождаются. Размер кучи задаётся при запуске приложения, но, в отличие от стека, он ограничен лишь физически, и это позволяет создавать динамические переменные.

Вы взаимодействуете с кучей посредством ссылок, обычно называемых указателями — это переменные, чьи значения являются адресами других переменных. Создавая указатель, вы указываете на местоположение памяти в куче, что задаёт начальное значение переменной и говорит программе, где получить доступ к этому значению. Из-за динамической природы кучи ЦП не принимает участия в контроле над ней; в языках без сборщика мусора (C, C++) разработчику нужно вручную освобождать участки памяти, которые больше не нужны. Если этого не делать, могут возникнуть утечки и фрагментация памяти, что существенно замедлит работу кучи.

Разработка на C++ с нуля в 2022 году: дорожная карта

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

Заключение

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

Стек (stack) и куча (heap) в Java

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

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

Стек в Java

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

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

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

Основные характеристики стека

Некоторые особенности стека:

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

Куча в Java

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

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

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

Young Generation (молодое поколение) – здесь размещаются и стареют все новые объекты. Минорная сборка мусора происходит, если эта область заполняется.

Old или Tenured Generation (старое или постоянное поколение) – здесь хранятся долгоживущие объекты. Когда объекты хранятся в молодом поколении, устанавливается порог возраста объекта, и когда этот порог достигается, объект перемещается в старое поколение.

Permanent Generation (постоянное поколение) – состоит из метаданных JVM для классов среды выполнения и методов приложения.

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

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

Ключевые особенности кучи в Java

Некоторые особенности кучи:

  • доступ осуществляется с помощью сложных методов управления памятью, которые включают в себя Young Generation, Old или Tenured Generation и Permanent Generation;
  • если место в куче заполнено, Java выдает ошибку java.lang.OutOfMemoryError;
  • доступ к этой памяти медленнее, чем к стеку;
  • эта память, в отличие от стека, не освобождается автоматически, ей нужен сборщик мусора, чтобы освободить неиспользуемые объекты для сохранения эффективности использования памяти;
  • в отличие от стека, куча не является потокобезопасной и должна быть защищена путем правильной синхронизации кода.

Пример

Основываясь на полученных знаниях, проанализируем простой код 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 String name = "John"; Person person = null; person = buildPerson(id, name); >>

Давайте проанализируем шаг за шагом:

  1. Когда входим в метод main(), в стеке создается место для хранения примитивов и ссылок этого метода.
    • Стек напрямую хранит примитивное значение integer id.
    • В стеке также будет создана ссылочная переменная person типа Person, которая будет указывать на реальный объект в куче.
  2. Вызов параметризованного конструктора Person(int, String) из main() выделит дополнительную память поверх предыдущего стека. Она будет хранить:
    • Ссылку на объект this вызывающего объекта в стеке
    • id примитивного значения в стеке
    • Ссылочную переменную String аргумента name, которая будет указывать на фактическую строку из пула строк в куче.
  3. Метод main() – это дальнейший вызов статического метода buildPerson(), для которого дальнейшее выделение будет происходить в стеке поверх предыдущего. Снова будут сохранены переменные в порядке, описанном выше.
  4. Однако в куче будут храниться все переменные экземпляра для вновь созданного объекта person типа Person.

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

Резюме

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

Параметр Стек Куча
Приложение Стек используется частями, по одному во время выполнения потока Все приложение использует кучу во время выполнения
Размер Стек имеет ограничения по размеру в зависимости от ОС и обычно меньше, чем куча Для кучи нет ограничений по размеру
Хранение Хранит только примитивные переменные и ссылки на объекты, созданные в куче Все вновь созданные объекты хранятся здесь
Доступ Доступ к нему осуществляется с использованием системы распределения памяти «последним пришел – первым вышел» (LIFO) Доступ к этой памяти осуществляется с помощью сложных методов управления памятью, включая Young Generation, Old или Tenured Generation и Permanent Generation
Продолжительность жизни Стек существует только до тех пор, пока работает текущий метод Куча существует до тех пор, пока работает приложение
Эффективность Гораздо быстрее выделить по сравнению с кучей Медленнее выделять по сравнению со стеком
Распределение/освобождение Эта память автоматически выделяется и освобождается при вызове и возврате метода Куча выделяется, когда новые объекты создаются и освобождаются сборщиком мусора, если на них больше нет ссылок

Заключение

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

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

почему Java stack memory быстрее heap

А он быстрее? По большому счету стэки, кучи и т.д. есть детали реализации конкретной виртуальной машины. Скорость, соответственно зависит от реализации и настроек.

17 окт 2017 в 19:13

@zRrr пишут что благодаря escape analysis локально созданные объекты нет смысла хранить в хипе , т.к. их область видимости — только метод, поэтому они могут храниться в стэке

17 окт 2017 в 19:27

@voipp в hotspot нет создания объектов на стеке, scalar replacement — это несколько другое: dev.cheremin.info/2016/02/…

17 окт 2017 в 20:01

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Управление памятью для стека тривиально: машина просто увеличивает или уменьшает одно значение, так называемый «указатель стека» (stack pointer). Управление памятью для кучи сложнее: память, выделенная в куче, освобождается в произвольные моменты, а каждая область выделенной в куче памяти может быть произвольного размера. Распределителю памяти, как правило, требуется приложить гораздо больше усилий для определения областей, которые можно использовать заново.

Отслеживать
ответ дан 17 окт 2017 в 18:32
HasmikGaryaka HasmikGaryaka
2,447 9 9 серебряных знаков 17 17 бронзовых знаков

в вопросе речь не о выделении, а о доступе. Наверное, все же по скорости доступа разницы быть не должно. И там, и там память.

17 окт 2017 в 18:34

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *