Java где хранится ссылка на объект

Java-модель памяти (часть 1)

Привет, Хабр! Представляю вашему вниманию перевод первой части статьи «Java Memory Model» автора Jakob Jenkov.

Прохожу обучение по Java и понадобилось изучить статью Java Memory Model. Перевёл её для лучшего понимания, ну а чтоб добро не пропадало решил поделиться с сообществом. Думаю, для новичков будет полезно, и если кому-то понравится, то переведу остальное.

Первоначальная Java-модель памяти была недостаточно хороша, поэтому она была пересмотрена в Java 1.5. Эта версия модели все ещё используется сегодня (Java 14+).

Внутренняя Java-модель памяти

Java-модель памяти, используемая внутри JVM, делит память на стеки потоков (thread stacks) и кучу (heap). Эта диаграмма иллюстрирует Java-модель памяти с логической точки зрения:

image

Каждый поток, работающий в виртуальной машине Java, имеет свой собственный стек. Стек содержит информацию о том, какие методы вызвал поток. Я буду называть это «стеком вызовов». Как только поток выполняет свой код, стек вызовов изменяется.

Стек потока содержит все локальные переменные для каждого выполняемого метода. Поток может получить доступ только к своему стеку. Локальные переменные, невидимы для всех других потоков, кроме потока, который их создал. Даже если два потока выполняют один и тот же код, они всё равно будут создавать локальные переменные этого кода в своих собственных стеках. Таким образом, каждый поток имеет свою версию каждой локальной переменной.

Все локальные переменные примитивных типов (boolean, byte, short, char, int, long, float, double) полностью хранятся в стеке потоков и не видны другим потокам. Один поток может передать копию примитивной переменной другому потоку, но не может совместно использовать примитивную локальную переменную.

Куча содержит все объекты, созданные в вашем приложении, независимо от того, какой поток создал объект. К этому относятся и версии объектов примитивных типов (например, Byte, Integer, Long и т.д.). Неважно, был ли объект создан и присвоен локальной переменной или создан как переменная-член другого объекта, он хранится в куче.

Ниже диаграмма, которая иллюстрирует стек вызовов и локальные переменные (они хранятся в стеках), а также объекты (они хранятся в куче):

image

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

Локальная переменная также может быть ссылкой на объект. В этом случае ссылка (локальная переменная) хранится в стеке потоков, но сам объект хранится в куче.

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

Читайте также:  All about pirates html

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

Статические переменные класса также хранятся в куче вместе с определением класса.

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

Диаграмма, которая иллюстрирует описанное выше:

image

Два потока имеют набор локальных переменных. Local Variable 2 указывает на общий объект в куче (Object 3). Каждый из потоков имеет свою копию локальной переменной со своей ссылкой. Их ссылки являются локальными переменными и поэтому хранятся в стеках потоков. Тем не менее, две разные ссылки указывают на один и тот же объект в куче.

Обратите внимание, что общий Object 3 имеет ссылки на Object 2 и Object 4 как переменные-члены (показано стрелками). Через эти ссылки два потока могут получить доступ к Object 2 и Object 4.

На диаграмме также показана локальная переменная (Local variable 1). Каждая её копия содержит разные ссылки, которые указывают на два разных объекта (Object 1 и Object 5), а не на один и тот же. Теоретически оба потока могут обращаться как к Object 1, так и к Object 5, если они имеют ссылки на оба этих объекта. Но на диаграмме выше каждый поток имеет ссылку только на один из двух объектов.

Итак, мы посмотрели иллюстрацию, теперь давайте посмотрим, как тоже самое выглядит в Java-коде:

Public class MyRunnable implements Runnable() < public void run() < methodOne(); >public void methodOne() < int localVariable1 = 45; MySharedObject localVariable2 = MySharedObject.sharedInstance; //. do more with local variables. methodTwo(); >public void methodTwo() < Integer localVariable1 = new Integer(99); //. do more with local variable. >>
public class MySharedObject < //статическая переменная, указывающая на экземпляр MySharedObject public static final MySharedObject sharedInstance = new MySharedObject(); // переменные-члены, указывающие на два объекта в куче public Integer object2 = new Integer(22); public Integer object4 = new Integer(44); public long member1 = 12345; public long member2 = 67890; >

Метод run() вызывает methodOne(), а methodOne() вызывает methodTwo().

methodOne() объявляет примитивную локальную переменную (localVariable1) типа int и локальную переменную (localVariable2), которая является ссылкой на объект.

Каждый поток, выполняющий методOne(), создаст свою собственную копию localVariable1 и localVariable2 в своих соответствующих стеках. Переменные localVariable1 будут полностью отделены друг от друга, находясь в стеке каждого потока. Один поток не может видеть, какие изменения вносит другой поток в свою копию localVariable1.

Читайте также:  Html margin как делать

Каждый поток, выполняющий методOne(), также создает свою собственную копию localVariable2. Однако две разные копии localVariable2 в конечном итоге указывают на один и тот же объект в куче. Дело в том, что localVariable2 указывает на объект, на который ссылается статическая переменная sharedInstance. Существует только одна копия статической переменной, и эта копия хранится в куче. Таким образом, обе копии localVariable2 в конечном итоге указывают на один и тот же экземпляр MySharedObject. Экземпляр MySharedObject также хранится в куче. Он соответствует Object 3 на диаграмме выше.

Обратите внимание, что класс MySharedObject также содержит две переменные-члены. Сами переменные-члены хранятся в куче вместе с объектом. Две переменные-члены указывают на два других объекта Integer. Эти целочисленные объекты соответствуют Object 2 и Object 4 на диаграмме.

Также обратите внимание, что methodTwo() создает локальную переменную с именем localVariable1. Эта локальная переменная является ссылкой на объект типа Integer. Метод устанавливает ссылку localVariable1 для указания на новый экземпляр Integer. Ссылка будет храниться в своей копии localVariable1 для каждого потока. Два экземпляра Integer будут сохранены в куче и, поскольку метод создает новый объект Integer при каждом выполнении, два потока, выполняющие этот метод, будут создавать отдельные экземпляры Integer. Они соответствуют Object 1 и Object 5 на диаграмме выше.

Обратите также внимание на две переменные-члены в классе MySharedObject типа long, который является примитивным типом. Поскольку эти переменные являются переменными-членами, они все еще хранятся в куче вместе с объектом. В стеке потоков хранятся только локальные переменные.

Источник

Что такое Heap и Stack память в Java?

Что такое Heap и Stack память в Java?

В Java есть такие понятия как Heap и Stack память. Сегодня мы узнаем разницу между ними и зачем они нужны. Как всегда, сначала в теории и затем на практике разберем эту сложную тему.

Java Heap память

Java Heap (куча) используется Java Runtime для выделения памяти под объекты и JRE классы. Создание нового объекта также происходит в куче. Здесь работает сборщик мусора: освобождает память путем удаления объектов, на которые нет каких-либо ссылок. Любой объект, созданный в куче, имеет глобальный доступ и на него могут ссылаться с любой части приложения.

Stack память в Java

Стековая память в Java работает по схеме LIFO (Последний-зашел-Первый-вышел). Всякий раз, когда вызывается метод, в памяти стека создается новый блок, который содержит примитивы и ссылки на другие объекты в методе. Как только метод заканчивает работу, блок также перестает использоваться, тем самым предоставляя доступ для следующего метода.
Размер стековой памяти намного меньше объема памяти в куче.

Давайте рассмотрим отличия стековой памяти и кучи на примере простой программы.

На картинке ниже представлена память стека и кучи для программы выше

Читайте также:  Php return array error

Java Heap Java Stack - javadevblog.com

А теперь рассмотри шаги выполнения нашей программы:

  1. Как только мы запустим программу, загружаются все классы среды выполнения в кучу. Потом метод main() находит строку 1 и Java Runtime создает стековую память для использования методом main().
  2. Далее в строке 2 создается int’овая переменная, которая хранится в памяти стека метода main() .
  3. Потом мы создали объект в строке 3 и он тут же появляется в куче, а стековая память содержит ссылку на него. Точно такой же процесс происходит, когда мы создаем объект Memory в строке 4 .
  4. Теперь в строке 5 мы вызываем метод exMethod() и тут же сразу создается блок на вершине стека, который будет использоваться этим методом. Поскольку в Java объекты и примитивы передаются по значению, то в строке 6 будет создана новая ссылка на объект, созданный в строке 3.
  5. Строка, созданная в строке 7 , отправляется в Пул строк (String Pool), который находится в куче. На эту строку также создается ссылка в стековой памяти метода exMethod() .
  6. Метод exMethod() завершается на строке 8 , поэтому блок стековой памяти для этого метода становится свободным.
  7. В строке 9 метод main() завершается, поэтому стековая память для метода main() будет уничтожена. Также программа заканчивается в этой строке, следовательно, Java Runtime освобождает всю память и завершает программу.

Разница между Stack и Heap памятью в Java

На основании приведенных выше объяснений, мы можем легко подытожить следующие различия между Heap и Stack памятью в Java.

  • Куча используется всеми частями приложения в то время как стек используется только одним потоком исполнения программы.
  • Всякий раз, когда создается объект, он всегда хранится в куче, а в памяти стека содержится ссылка на него. Память стека содержит только локальные переменные примитивных типов и ссылки на объекты в куче.
  • Объекты в куче доступны с любой точки программы, в то время как стековая память не может быть доступна для других потоков.
  • Управление памятью в стеке осуществляется по схеме LIFO .
  • Стековая память существует лишь какое-то время работы программы, а память в куче живет с самого начала до конца работы программы.
  • Мы можем использовать -Xms и -Xmx опции JVM, чтобы определить начальный и максимальный размер памяти в куче. Для стека определить размер памяти можно с помощью опции -Xss .
  • Если память стека полностью занята, то Java Runtime бросает java.lang.StackOverflowError , а если память кучи заполнена, то бросается исключение java.lang.OutOfMemoryError: Java Heap Space .
  • Размер памяти стека намного меньше памяти в куче. Из-за простоты распределения памяти ( LIFO ), стековая память работает намного быстрее кучи.

Вот и все, что нужно знать о Stack и Heap памяти в Java. Следите за обновлениями в разделе Полезности.

Источник

Оцените статью