Введение
Как вы знаете, Java — это объектно-ориентированный язык программирования. Когда мы обычно пишем код, мы постоянно манипулируем различными объектами, поэтому когда вы пишете кодUser user = new User();Что такое JVM, когда это строка кода?
Во-вторых, понять объект
1. Схема памяти
Структура памяти объекта в виртуальной машине Hotspot разделена на три части: заголовок объекта, данные экземпляра и заполнение выравнивания.
-
Заголовок объекта состоит из двух частей информации: первая часть используется для хранения текущих данных самого объекта (HashCode, возраст генерации сборщика мусора, флаг состояния блокировки и т. д.). Другая часть — это указатель типа, который указывает на метаданные своего класса, и виртуальная машина использует этот указатель, чтобы определить, экземпляром какого класса является объект (если используется метод пула дескрипторов, его не будет). Если это массив, также будет запись длины массива, как показано в следующей таблице:
содержание инструкция Mark Word Хэш-код объекта или информация о блокировке и т. д. Class Metadata Address указатель данных типа объекта Array length длина массива Mark Word — это нефиксированная структура данных, которая позволяет хранить как можно больше информации на очень маленьком пространстве, повторно используя собственное пространство для хранения в соответствии с состоянием объекта. Содержимое хранилища в каждом состоянии показано в следующей таблице: бит флага государство :-------: :------------: 01 не заблокирован 00 Легкий замок 10 Блокировка в супертяжелом весе 11 маркеры ГХ 01 может быть предвзятым -
Часть данных экземпляра — это эффективная информация, которая действительно хранится, то есть содержимое различных типов полей, определенных в коде. Унаследовано ли оно от родительского класса или в дочернем классе.
-
Заполнение выравнивания не требуется, оно действует только как заполнитель, потому что виртуальная машина HotSpot требует, чтобы начальный адрес объекта был целым числом, кратным 8 байтам.
2. Доступ к объекту
В программе на Java мы манипулируем объектом через ссылку на этот объект. Все мы знаем, что объект существует в куче, а эта ссылка существует в стеке виртуальной машины. Так как же ссылка используется для определения местоположения объекта в куче?
- Метод прямого указателя (реализация HotSpot): адрес объекта в куче хранится непосредственно в ссылке. Преимущество состоит в том, что скорость позиционирования высока, а недостаток в том, что необходимо изменить саму ссылку на движение объекта (движение объекта во время GC).
- Метод дескриптора: часть кучи Java разделена как пул дескрипторов.Ссылка хранит адрес дескриптора объекта, а дескриптор включает конкретную информацию о местоположении экземпляра объекта и типа. Преимущество в том, что перемещение объекта изменит только указатель данных экземпляра в дескрипторе, недостатком является два позиционирования.
3. Создать объектный процесс
Основная информация об объекте представлена выше, теперь поговорим о самом процессе создания объекта:
- Когда виртуальная машина встречает новую инструкцию, она проверяет, могут ли параметры этой инструкции найти символическую ссылку на класс в пуле констант, и проверяет, загружен ли представленный класс загрузчиком классов. Если он не загружен, то сначала должен быть загружен класс.
- После того, как проверка загрузки класса пройдена, виртуальная машина выделит память для нового объекта, а размер памяти, необходимой объекту, можно будет определить после загрузки класса.
- После того, как выделение памяти завершено, виртуальная машина должна инициализировать объект нулевым значением, чтобы гарантировать, что объектпеременная экземпляраЕго можно использовать непосредственно в коде без присвоения начального значения. Переменные класса инициализируются нулевым значением на этапе подготовки загрузки класса.
- Установите необходимую информацию для заголовка объекта, например, как найти информацию о метаданных класса, HashCode объекта и возраст создания GC.
- После вышеперечисленных операций новый объект создан, но метод
не выполнен, а все поля равны нулю. В это время необходимо выполнить метод (метод конструктора) для инициализации объекта в соответствии с пожеланиями программиста. Операция инициализации переменных класса завершается в методе на этапе инициализации загрузки класса.
Существует два способа выделения памяти:
- Организуется память кучи Java (с помощью разметки или сборщика мусора с уплотнением), с помощью указателя на свободное место, выделение памяти и перемещение указателя на расстояние, равное выделенному размеру
- Память не регулярна (используется сборщик мусора с подчисткой по меткам), виртуальная машина ведет список доступных блоков памяти, находит место в памяти достаточно большое, чтобы разделить объект из списка при выделении памяти и обновляет список доступной памяти.
GC запускается, когда не может быть найдено достаточно памяти
Решение проблемы параллелизма при выделении памяти:
- Синхронизируйте действие по выделению памяти — используйте метод повторной попытки CAS при сбое, чтобы обеспечить атомарность операции обновления.
- Каждый поток предварительно выделяет небольшой объем памяти в куче, который называется локальным буфером выделения потока (TLAB). Какой поток должен выделить память в своем TLAB. Блокировка синхронизации требуется только тогда, когда TLAB израсходован и выделяется новый TLAB. Задается параметром -XX:+/-UseTLAB.
В-четвертых, создайте проблему переупорядочивания инструкций объекта.
A a = new A();
Простое действие декомпозиции нового объекта:
- Выделить место в памяти для объекта
- Инициализировать объект
- Установите ссылку, указывающую на выделенный адрес памяти
Среди них переупорядочение инструкций произойдет между шагами 2 и 3, что вызовет проблемы, если доступ к объекту будет осуществлен до инициализации в многопоточности.Эта проблема существует в режиме блокировки двойного обнаружения одноэлементного режима. Вы можете использовать volatile, чтобы предотвратить переупорядочивание инструкций для решения проблемы;