1. Введение
Для оптимального запуска приложения JVM делит память на стек и динамическую память. Всякий раз, когда мы объявляем новые переменные и объекты, вызываем новый метод, объявляем строку
или выполняем аналогичные операции, JVM выделяет память для этих операций либо из памяти стека, либо из пространства кучи.
В этом уроке мы рассмотрим эти модели памяти. Во-первых, мы рассмотрим их ключевые особенности. Затем мы узнаем, как они хранятся в оперативной памяти и где их использовать. Наконец, мы обсудим ключевые различия между ними.
2. Память стека в Java
Память стека в Java используется для выделения статической памяти и выполнения потока. Он содержит примитивные значения, характерные для метода, и ссылки на объекты, на которые ссылается метод, находящиеся в куче.
Доступ к этой памяти осуществляется в порядке «последним поступил – первым обслужен» (LIFO). Всякий раз, когда мы вызываем новый метод, поверх стека создается новый блок, который содержит значения, характерные для этого метода, такие как примитивные переменные и ссылки на объекты.
Когда метод завершает выполнение, соответствующий кадр стека сбрасывается, поток возвращается к вызывающему методу, и освобождается место для следующего метода.
2.1. Основные характеристики стековой памяти
Некоторые другие особенности стековой памяти включают в себя:
- Он увеличивается и уменьшается по мере вызова и возврата новых методов соответственно.
- Переменные внутри стека существуют только до тех пор, пока работает создавший их метод.
- Он автоматически выделяется и освобождается, когда метод завершает выполнение.
- Если эта память заполнена, Java выдает
ошибку java.lang.StackOverFlowError.
- Доступ к этой памяти быстрый по сравнению с динамической памятью.
- Эта память является потокобезопасной, поскольку каждый поток работает в своем собственном стеке.
3. Пространство кучи в Java
Пространство кучи используется для динамического выделения памяти для объектов Java и классов JRE во время выполнения . Новые объекты всегда создаются в куче, а ссылки на эти объекты хранятся в памяти стека.
Эти объекты имеют глобальный доступ, и мы можем получить к ним доступ из любой точки приложения.
Мы можем разбить эту модель памяти на более мелкие части, называемые поколениями, а именно:
- Young Generation – здесь размещаются и стареют все новые объекты. Незначительная сборка мусора происходит, когда он заполняется.
- Старое или постоянное поколение — здесь хранятся долгоживущие объекты. Когда объекты хранятся в молодом поколении, устанавливается порог возраста объекта, и когда этот порог достигается, объект перемещается в старое поколение.
- Постоянная генерация — состоит из метаданных 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);
}
}
Давайте проанализируем это шаг за шагом:
- Когда мы входим в метод
main()
, в памяти стека создается место для хранения примитивов и ссылок этого метода.
- Память стека напрямую хранит примитивное значение целочисленного
идентификатора.
- В памяти стека также будет создана ссылочная переменная
person
типаPerson , которая будет указывать на реальный объект в куче.
- Вызов параметризованного конструктора
Person(int, String)
изmain()
выделит дополнительную память поверх предыдущего стека. Это будет хранить:
- Ссылка на
этот
объект вызывающего объекта в памяти стека Идентификатор
примитивного значения в памяти стека- Ссылочная переменная имени аргумента
String
,
которая будет указывать на фактическую строку из пула строк в куче памяти.
- Основной метод — это дальнейший вызов статического метода
buildPerson
()
, для которого дальнейшее выделение будет происходить в памяти стека поверх предыдущего. Это снова сохранит переменные в порядке, описанном выше. - Однако в памяти кучи будут храниться все переменные экземпляра для вновь созданного объекта
person
типаPerson.
Давайте посмотрим на это распределение на диаграмме ниже:
5. Резюме
Прежде чем мы завершим эту статью, давайте кратко суммируем различия между памятью стека и пространством кучи:
| Параметр | Память стека | Куча пространства |
| Заявление | Стек используется частями, по одному во время выполнения потока | Все приложение использует пространство кучи во время выполнения. |
| Размер | Стек имеет ограничения по размеру в зависимости от ОС и обычно меньше, чем куча. | Для кучи нет ограничений по размеру |
| Хранилище | Хранит только примитивные переменные и ссылки на объекты, созданные в Heap Space. | Все вновь созданные объекты хранятся здесь |
| Заказ | Доступ к нему осуществляется с использованием системы распределения памяти «последним пришел — первым обслужен» (LIFO). | Доступ к этой памяти осуществляется с помощью сложных методов управления памятью, включая «молодое поколение», «старое или постоянное поколение» и «постоянное поколение». |
| Жизнь | Память стека существует только до тех пор, пока работает текущий метод. | Пространство кучи существует до тех пор, пока работает приложение |
| Эффективность | Гораздо быстрее выделить по сравнению с кучей | Медленнее выделять по сравнению со стеком |
| Распределение/освобождение | Эта память автоматически выделяется и освобождается при вызове и возврате метода соответственно. | Пространство кучи выделяется, когда новые объекты создаются и освобождаются Gargabe Collector, когда на них больше не ссылаются. |
6. Заключение
Стек и куча — это два способа распределения памяти в Java. В этой статье мы узнали, как они работают и когда их использовать для разработки лучших программ на Java.
Чтобы узнать больше об управлении памятью в Java, ознакомьтесь с этой статьей здесь . Мы также затронули сборщик мусора JVM, который кратко обсуждается в этой статье .