Изучите структуру памяти JVM по крупицам

Java задняя часть JVM C++

предисловие

Я пытался вести блог разными словами! Причина очень проста: все книги знаний, о которых вы говорите, есть, так почему же все предпочитают читать ваши сообщения в блогах, а не читать книги, чтобы учиться? Поскольку содержание книги насыщено описательным текстом, знание jvm чрезвычайно скучно, но его трудно освоить! Этим как раз и объясняется популярность книг серии Head First: графические знания усваиваются у всех гораздо быстрее, чем текстовые. В будущем я также постараюсь использовать свои особенности для ведения блога, постараюсь изо всех сил вызвать интерес читателей, и я буду удовлетворен, если смогу чему-то научиться из этого!

Сегодня я постепенно исследую серию JVM и планирую рассмотреть структуру памяти jvm! Что же касается пользы от изучения этих знаний? Во-первых, с точки зрения собеседования, если вы понимаете jvm и имеете прочную основу Java, вы будете более конкурентоспособны (потому что я еще не получил степень бакалавра, поэтому я часто рассматриваю вопросы с точки зрения интервьюера) . Во-вторых, улучшите свое понимание Java и узнайте, где находится каждый объект и каждая создаваемая вами переменная. Ну, без лишних слов, приступим к делу!

до начала

Между Java и C++ существует «стена», окруженная технологиями динамического выделения памяти и сборки мусора, люди за стеной хотят войти, а люди внутри стены хотят выйти. Может быть вы часто видите StackOverFlowError, OutOfMemoryError не может запуститься, потому что не знаете, что вызывает взрыв памяти, конечно же, вы не можете это решить!

Возьмем простой пример

public class test {
    private int f() {
        f();
    }
    public static void main(String[] args) {
        f();
    }
}

Эта простая рекурсия, нет, она не рекурсивная, потому что нет условия завершения, но вы знаете, какую ошибку она в итоге сообщит и почему она сообщит об этой ошибке? Что именно не так с этой памятью?

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

Цель

Вам может быть интересно, что вы узнаете после прочтения этой статьи?

Знайте, где будут размещены ваши объекты (не совсем) Понимать, какие области являются частными для потоков, а какие — общими для потоков. Есть идеи, что случилось с вызовом метода? … Подождите, вы могли бы также объяснить некоторые странные проблемы, которые у вас были в прошлом! Одним словом, если вы раньше не изучали эти знания, то эти вещи для вас рост!

мир внутри стен

Вам может быть интересно, что именно находится внутри стены? Следуйте за мной, чтобы узнать

На приведенном выше рисунке показано более подробное разделение памяти JVM.Давайте разделим область памяти JVM в соответствии с приватным разделением потоков.

Давайте сосредоточимся на этих областях памяти.

Регистр счетчика программ

Что такое программный счетчик?Каждый, кто изучал ассемблер, знает, что физический адрес, составленный из cs:ip, является адресом следующей выполняемой инструкции. посмотрите на картинку

Мы ясно видим, что адрес памяти, на который указывает текущий cs:ip, является именно тем местом, где находится следующая инструкция, которую мы хотим выполнить. что программный счетчик — Threads are private, подумайте о моем примере cs:ip, мы можем, естественно, думать, что программный счетчик на самом деле записывает, какую инструкцию выполняет поток в данный момент, потому что зачем записывать это значение? Потому что, если у нас много потоков, порядок выполнения потоков непредсказуем.Если мы выполняем инструкции в потоке A в определенное время, то поток B получает ресурсы процессора для выполнения инструкций потоку B. Если после другого Период времени A снова получил ресурсы процессора и хочет вернуться к траве.В это время он возвращается к потоку A для выполнения, и он не знает, какую инструкцию потока A выполнить! Это неловкая ситуация без счетчика программ, а с счетчиком программ в потоке, этой проблемы не существует.Вот почему появляется счетчик программ, и его полезность.Я думаю, что после прочтения этого текста у вас должно быть полное понимание концепции счетчика программ!

Кроме того, я должен подчеркнуть, что счетчик программ — это единственная область в спецификации виртуальной машины Java, в которой не указаны ошибки памяти!

Стек виртуальных машин (стек виртуальных машин)

Для чего эта площадь? Почему это также частный поток?

Стек виртуальной машины описывает модель памяти выполнения методов Java. Давайте интерпретируем это предложение: почему Vm Stack — это модель памяти, описывающая выполнение методов Java? фактически:

При выполнении каждого метода будет создаваться фрейм стека (Stack Frame) Те, кто изучал c/c++, должны быть знакомы с этой концепцией. Фрейм стека используется для хранения таблицы локальных переменных, стека операндов, динамической ссылки, информации о выходе из метода и т. д. Процесс каждого метода от начала до конца вызова соответствует процессу проталкивания и выталкивания из стека в этом Vm Stack! Это также может ответить на вопрос, который мы видели в начале, очень просто, что ошибка должна быть StackOverFlowError в однопоточном случае и OutOfMemoryError в многопоточном случае (на картинке выше написано ясно)

Например

public void test() {
    String name = "stormma";
    int age = 21;
}
JAVA架构群:678779467

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

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

Таблица локальных переменных: таблица локальных переменных представляет собой набор областей хранения значений переменных, используемых для хранения параметров метода и локальных переменных, определенных внутри метода. Типы данных, хранящихся в них, представляют собой различные базовые типы данных и ссылки на объекты, известные во время компиляции. (returnAddress) тип (который указывает на адрес инструкции байт-кода). Объем памяти, необходимый для таблицы локальных переменных, рассчитывается во время компиляции, то есть, когда программа на Java компилируется в файл класса, определяется емкость максимальной таблицы локальных переменных, которая должна быть выделена. При входе в метод полностью определяется, сколько места для локальных переменных метод должен выделить в стеке, и размер таблицы локальных переменных не меняется во время выполнения метода.

Стек операндов. Стек операндов часто называют стеком операций, и максимальная глубина стека операндов также определяется во время компиляции. Емкость стека, занимаемая 32-битными типами данных, равна 1, а емкость стека, занимаемая 64-битными типами данных, равна 2. Когда метод начинает выполняться, его операционный стек пуст.Во время выполнения метода будут различные инструкции байт-кода (такие как: операция сложения, вычисление элемента присваивания и т. д.) для записи и извлечения содержимого в операционный стек, то есть операции push и pop. Механизм выполнения интерпретации виртуальной машины Java называется «механизм выполнения на основе стека», а упомянутый «стек» - это стек операндов. Поэтому мы также называем виртуальную машину Java основанной на стеке, что отличается от виртуальной машины Android, основанной на регистрах. Основным преимуществом набора инструкций на основе стека является его сильная переносимость, а основным недостатком является относительно низкая скорость выполнения; и поскольку регистры предоставляются непосредственно аппаратным обеспечением, основным преимуществом набора инструкций на основе регистров является скорость выполнения.Недостатком является плохая переносимость

Динамическое связывание: каждый кадр стека содержит ссылку на метод, которому принадлежит кадр стека, в пуле констант времени выполнения (в области методов, описанной ниже).Эта ссылка сохраняется для поддержки динамического связывания во время вызова метода. В константном пуле файла класса имеется большое количество символических ссылок, и инструкция вызова метода в байт-коде принимает в качестве параметра символическую ссылку на метод из константного пула. Некоторые из этих символических ссылок будут преобразованы в прямые ссылки (такие как final, статические поля и т. д.) на этапе загрузки класса или при их первом использовании, что называется статическим разрешением, а другая часть будет преобразована в прямые ссылки во время каждого запуска. Эта часть называется динамической компоновкой.

Адрес возврата метода: когда метод выполняется, есть два способа выйти из метода: механизм выполнения встречает инструкцию байт-кода, возвращаемую любым методом, или сталкивается с исключением, и это исключение не обрабатывается в теле метода. Независимо от того, какой метод выхода используется, после выхода из метода он должен вернуться в место, где метод был вызван, прежде чем программа сможет продолжить выполнение. Когда метод возвращается, ему может потребоваться сохранить некоторую информацию в кадре стека, чтобы помочь восстановить состояние выполнения его метода верхнего уровня. Вообще говоря, когда метод завершается нормально, значение счетчика ПК вызывающей стороны может быть использовано в качестве адреса возврата, который, вероятно, будет сохранен во фрейме стека.При аварийном завершении метода адрес возврата определяется обработчиком исключений. ., эта часть информации вообще не сохраняется в кадре стека. Процесс выхода из метода фактически эквивалентен выходу из текущего кадра стека, поэтому возможные операции при выходе: восстановить таблицу локальных переменных и стек операндов метода верхнего уровня, и если есть возвращаемое значение, поместить его в кадр стека вызывающей стороны В стеке операндов отрегулируйте значение счетчика ПК, чтобы оно указывало на инструкцию после инструкции вызова метода. Я думаю, что рассказал все об этой области, и я думаю, что вы тоже должны это понять.

Теперь давайте перейдем к следующей области: куча

куча

Область кучи — это очень интересная область, почему она интересна, потому что эта область является общей для всех потоков, а также является поселением большинства наших объектов (почему именно большинством? Это понятие будет рассмотрено в наших следующих статьях. Для подробного объяснения, если вам особенно любопытно, вы можете прочитать мою предыдущую статью, Java Escape Analysis)! Это также самый большой кусок памяти, управляемый jvm (кстати, размер на картинке выше не отражает долю памяти, просто для удобства просмотра)!Это также основная область для работы gc.

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

Meta-Area

Эта область также является областью, разделяемой потоками.В ней в основном хранится информация о классе загруженного класса jvm, переменные класса, константы (это находится в константной области метаобласти) и код, скомпилированный реальным компилятор времени.

постоянная область времени выполнения

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

После прочтения скучных концептуальных вещей, давайте посмотрим на пример, чтобы углубить впечатление от этого произведения:

public void test() {
    Object obj = new Object();
}
JAVA架构群:678779467

Для этого кода будут задействованы три наиболее важные области памяти Vm Stack, Java Heap и Meta-Area.

В сочетании с нашим предыдущим примером, поскольку метод test() задействует область Vm Stack, я думаю, вы должны понимать, что obj будет храниться в таблице локальных переменных, new Object(), как мы уже говорили ранее, большинство наших объектов будет хранится в Java Heap в этой области, поэтому Java Heap хранит этот объект экземпляра! Тогда вам будет любопытно, почему Meta-Area задействована?

Мы знаем, что в Meta-Area хранится информация о классе, константы переменных класса и многое другое! Потому что, когда мы создаем экземпляр соответствующего объекта, нам нужно использовать информацию класса Object, поэтому он будет обращаться к объекту класса Object.class метаобласти, чтобы получить некоторые вещи, необходимые для создания экземпляра объекта.

Кстати, в качестве дополнения, я думаю, вам также нужно знать, как вы можете получить доступ к инстанцированному объекту в области Java Heap по ссылке obj

Есть два способа: один использует указатель дескриптора (вы должны быть знакомы с этими понятиями после изучения c/c++)

Есть и прямой доступ через указатель

Изображение выше взято из книги Deep Understanding of the JVM.

Стек собственных методов

Эта область относительно менее важна, чем предыдущие концепции.

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

Например, Java вызывает c/c++/assembly для использования этой области.

конец

Я думаю, что после прочтения этого сообщения в блоге вы должны были достичь нашей цели еще до того, как статья началась! Введение в эту статью относительно простое и основано на использовании примеров для объяснения роли области памяти, поэтому я думаю, что вы с большей вероятностью примете ее, и она заинтересует вас больше, чем большой объем информации. масштаб текстовое описание! Если у вас есть какие-либо предложения или сомнения, вы можете связаться со мной через GitHub!