Что такое менеджер памяти java

Модель памяти Java (JVM) – Управление памятью на Java

Управление памятью на Java. Модель памяти Java. Модель памяти JVM. Распределение памяти Java, Молодое поколение, Старое поколение, Постоянное поколение, Куча, Стек.

Модель памяти Java (JVM)

Управление памятью на Java – Молодое поколение

Важные моменты, касающиеся пространств для молодого поколения:

Управление памятью в Java – Старое поколение

Память старого поколения содержит объекты, которые являются долгоживущими и сохранились после многих раундов незначительной GC. Обычно сборка мусора выполняется в памяти старого поколения, когда она заполнена. Сборка мусора старого поколения называется Major GC и обычно занимает больше времени.

Остановите Мировое событие

Все сборки мусора являются событиями “Остановить мир”, потому что все потоки приложений останавливаются до завершения операции.

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

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

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

Модель памяти Java – Постоянное поколение

Постоянное поколение или “PermGen” содержит метаданные приложения, необходимые JVM для описания классов и методов, используемых в приложении. Обратите внимание, что PermGen не является частью памяти кучи Java.

Пермское поколение заполняется JVM во время выполнения на основе классов, используемых приложением. Perm Gen также содержит классы и методы библиотеки Java SE. Объекты пермского поколения-это мусор, собранный в полной сборке мусора.

Модель памяти Java – Область Методов

Область методов является частью пространства в PermGen и используется для хранения структуры классов (констант времени выполнения и статических переменных) и кода для методов и конструкторов.

Модель памяти Java – Пул памяти

Пулы памяти создаются менеджерами памяти JVM для создания пула неизменяемых объектов, если реализация поддерживает это. Пул строк-хороший пример такого пула памяти. Пул памяти может принадлежать куче или PermGen, в зависимости от реализации диспетчера памяти JVM.

Модель памяти Java – Пул констант времени Выполнения

Пул констант времени выполнения-это представление во время выполнения для каждого класса пула констант в классе. Он содержит константы времени выполнения класса и статические методы. Пул констант времени выполнения является частью области методов.

Модель памяти Java – Память стека Java

Управление памятью в Java – Переключатели памяти кучи Java

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

-XmsДля установки начального размера кучи при запуске JVM
-XmxДля установки максимального размера кучи.
-XmnДля определения численности молодого поколения остальная часть пространства отводится Старому поколению.
-XX:ПермГенДля установки начального размера постоянной памяти поколения
-XX:MaxPermGenДля установки максимального размера PermGen
-XX:ВыживаниеДля обеспечения соотношения пространства Эдема и пространства Выживших, например, если размер молодого поколения составляет 10 м, а переключатель виртуальной машины равен 5 м, то для пространства Эдема будет зарезервировано 5 м, а для обоих пространств Выживших-по 2,5 м. Значение по умолчанию равно 8.
-XX:Новое времяДля обеспечения соотношения размеров старого/нового поколения. Значение по умолчанию равно 2.

Управление памятью в Java – Сборка мусора Java

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

Один из основных способов сбора мусора включает в себя три этапа:

Есть две проблемы с простым подходом к пометке и удалению.

Вышеуказанные недостатки при простом подходе являются причиной того, что Сборка мусора Java является поколенческой и у нас есть Молодое поколение и Старое поколение места в памяти кучи. Я уже объяснял выше, как объекты сканируются и перемещаются из одного пространства поколений в другое на основе второстепенного GC и Основного GC.

Управление памятью в Java – Типы сборки мусора Java

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

Управление памятью в Java – Мониторинг сборки мусора Java

Мы можем использовать командную строку Java, а также инструменты пользовательского интерфейса для мониторинга действий по сбору мусора в приложении. Для моего примера я использую одно из демонстрационных приложений, предоставляемых загрузками Java SE.

Команда, используемая мной для запуска демонстрационного приложения, является:

jstat

Мы можем использовать jstat инструмент командной строки для мониторинга памяти JVM и операций по сбору мусора. Он поставляется со стандартным JDK, так что вам не нужно ничего делать, чтобы его получить.

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

Давайте пройдемся по каждой колонке по очереди.

Java VisualVM с визуальным GC

Если вы хотите видеть операции с памятью и GC в графическом интерфейсе, вы можете использовать jvisualvm инструмент. Java VisualVM также является частью JDK, поэтому вам не нужно загружать его отдельно.

Если вы видите много полных операций GC, то вам следует попробовать увеличить объем памяти старого поколения.

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

Это все для модели памяти Java, управления памятью в Java и сбора мусора, я надеюсь, что это поможет вам понять память JVM и процесс сбора мусора.

Источник

Java Memory Model

Модель памяти Java или Java Memory Model (JMM) описывает поведение программы в многопоточной среде. Она объясняет возможное поведение потоков и то, на что должен опираться программист, разрабатывающий приложение.

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

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

Неправильно синхронизированные программы могут приводить к неожиданным результатам.

Thread 1Thread 2
1: r2 = A;3: r1 = B;
2: B = 1;4: A = 2;

Может показаться, что результат r2 == 2 и r1 == 1 невозможен, так как либо инструкция 1 должна быть первой, либо инструкция 3 должна быть первой. Если инструкция 1 будет первой, то она не сможет увидеть число 2, записанное в инструкции 4. Если инструкция 3 будет первой, то она не сможет увидеть результат инструкции 2.

Если какое-то выполнение программы привело бы к такому поведению, то мы бы знали, что инструкция 4 была до инструкции 1, которая была до инструкции 2, которая была до инструкции 3, которая была до инструкции 4, что совершенно абсурдно.

Однако современным компиляторам разрешено переставлять местами инструкции в обоих потоках в тех случаях, когда это не затрагивает исполнение одного потока не учитывая другие потоки. Если инструкция 1 и инструкция 2 поменяются местами, то мы с лёгкостью сможем получит результат r2 == 2 и r1 == 1.

Thread 1Thread 2
B = 1;r1 = B;
r2 = A;A = 2;

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

Ситуация, описанные в примере выше, называется «состоянием гонки» или Data Race.

Переставлять команды может Just-In-Time компилятор или процессор. Более того, каждое ядро процессора может иметь свой кеш. А значит, у каждого процессора может быть своё значение одной и той же переменнной, что может привести к аналогичным результатам.

Модель памяти описывает, какие значения могут быть считаны в каждый момент программы. Поведение потока в изоляции должно быть таким, каким описано в самом потоке, но значения, считываемые из переменных определяются моделью памяти. Когда мы ссылаемся на это, то мы говорим, что программа подчиняется intra-thread semantic, то есть семантики однопоточного приложения.

Разделяемые переменные

Память, которая может быть совместно использована разными потоками, называется куча (shared memory или heap memory).

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

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

Два доступа к одной переменной называются конфликтующими, если хотя бы один их доступов меняет значение переменной (другой может как менять, так и считывать текущее значение).

Действия

Inter-thread action (термин такой, не знаю, как перевести, может, межпоточное действие?) — это действие внутри одного потока, которое может повлиять или быть замечено другим потоком. Существует несколько типов inter-thread action:

Program order

Program order (лучше не переводить, чтобы не возникло путаницы) — общий порядок потока, выполняющего действия, который отражает порядок, в котором должны быть выполнены все действия с соответствии с семантикой intra-thread semantic потока.

Действия называются sequentially consistent (лучше тоже не переводить), если все действия выполняются в общем порядке, который соответствует program order, а также каждое чтение переменной видит последнее значение, записанное туда до этого в соответствии с порядком выполнения.

Если в программе нет состояния гонки, то все запуски программы будут sequentially consistent.

Synchronization order

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

Действия по синхронизации вводят связь synchronized-with (синхронизировано с):

Happens-before

Happens-before («выполняется прежде» или «произошло-до») — отношение порядка между атомарными командами. Оно означает, что вторая команда будет видеть изменения первой команды, и что первая команды выполнилась перед второй. Рекомендую ознакомиться с многопоточностью в Java, перед продолжением чтения.

Работа с final полями

Все final поля должны быть инициализированы либо конструкциями инициализации, либо внутри конструктора. Не стоит внутри конструкторов обращаться к другим потокам. Поток увидит ссылку на объект только после полной инициализации, то есть по окончании работы конструктора. Так как final полям присваивается значение только один раз, то просто не обращайтесь к другим потоком внутри конструкторов и блоков инициализации и проблем возникнуть не должно.

Однако final поля могут быть изменены через Java Reflection API, чем пользуются, например, десериализаторы. Просто не отдавайте ссылку на объект другим потокам и не читайте значение final поля до его обновления и всё будет нормально.

Word tearing

Некоторые процессоры не позволяют записывать один байт в ОЗУ, что приводит к проблеме, называемой word tearing. Представьте, что у нас есть массив байт. Один поток записывает первый байт, а второй поток пытается записать значение в рядом стоящий байт. Но если процессор не может записать один байт, а только целое машинное слово, то запись рядом стоящего байта может быть проблематичной. Если просто считать машинное слово, обновить один байт и записать обратно, то мы помешаем другому потоку.

В JVM нет проблемы word tearing. Два потока, пишущие рядом стоящие байты не должны мешать друг другу.

Источник

Java Blog

Модель памяти Java (JMM, Java Memory Model)

Модель памяти Java, используемая внутри JVM, разделяет память между стеками потоков и кучей (heap). Каждый поток, выполняющийся в виртуальной машине Java, имеет свой собственный стек потока. Стек потока содержит информацию о том, какие методы вызвал поток для достижения текущей точки выполнения.

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

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

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

Источник

Развеиваем мифы об управлении памятью в JVM

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

Структура памяти JVM

Сначала давайте посмотрим на структуру памяти JVM. Эта структура применяется начиная с JDK 11. Вот какая память доступна процессу JVM, она выделяется операционной системой:

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

Это нативная память, выделяемая ОС, и её размер зависит от системы, процессор и JRE. Какие области и для чего предназначены?

Куча (heap)

Здесь JVM хранит объекты и динамические данные. Это самая крупная область памяти, в ней работает сборщик мусора. Размером кучи можно управлять с помощью флагов Xms (начальный размер) и Xmx (максимальный размер). Куча не передаётся виртуальной машине целиком, какая-то часть резервируется в качестве виртуального пространства, за счёт которого куча может в будущем расти. Куча делится на пространства «молодого» и «старого» поколения.

Стеки потоков исполнения

Метапространство

Кеш кода

Здесь компилятор Just In Time (JIT) хранит скомпилированные блоки кода, к которым приходится часто обращаться. Обычно JVM интерпретирует байткод в нативный машинный код, однако код, скомпилированный JIT-компилятором, не нужно интерпретировать, он уже представлен в нативном формате и закеширован в этой области памяти.

Общие библиотеки

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

Использование памяти JVM: стек и куча

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

Здесь вы можете увидеть, как исполняется вышеприведённая программа и как используются стек и куча:

Управление памятью JVM: сборка мусора

Давайте разберёмся с автоматическим управлением кучей, которое играет очень важную роль с точки зрения производительности приложения. Когда программа пытается выделить в куче больше памяти, чем доступно (в зависимости от значения Xmx ), мы получаем ошибки нехватки памяти.

JVM управляет куче с помощью сборки мусора. Чтобы освободить место для создания нового объекта, JVM очищает память, занятую потерянными объектами, то есть объектами, на которые больше нет прямых или опосредованных ссылок из стека.

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

Сборщик мусора в JVM отвечает за:

Сборщик мусора Mark & Sweep

JVM использует отдельный поток демона, который работает в фоне для сборки мусора. Этот процесс запускается при выполнении определённых условий. Сборщик Mark & Sweep обычно работает в два этапа, иногда добавляют третий, в зависимости от используемого алгоритма.

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

JVM предлагает на выбор несколько разных алгоритмов сборки мусора, и в зависимости от вашего JDK может быть ещё больше вариантов (например, сборщик Shenandoah в OpenJDK). Авторы разных реализаций стремятся к разным целям:

Сборщики в JDK 11

Процесс сборки мусора

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

Младший сборщик

Он поддерживает чистоту и компактность пространства молодого поколения. Запускается тогда, когда JVM не может получить в раю необходимую память для размещения нового объекта. Изначально все области кучи пусты. Рай заполняется первым, за ним область выживших, и в конце хранилище.

Здесь вы можете увидеть процесс работы этого сборщика:

Старший сборщик

Следит за чистотой и компактностью пространства старого поколения (хранилищем). Запускается при одном из таких условий:

Заключение

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

Но для большинства JVM-разработчиков (Java, Kotlin, Scala, Clojure, JRuby, Jython) этого объёма информации будет достаточно. Надеюсь, теперь вы сможете писать более качественный код, создавать более производительные приложения, избегая различных проблем с утечкой памяти.

Источник

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

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

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

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

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

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

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

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

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

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

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

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

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

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

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

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

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

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

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

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

Что такое менеджер памяти java. Смотреть фото Что такое менеджер памяти java. Смотреть картинку Что такое менеджер памяти java. Картинка про Что такое менеджер памяти java. Фото Что такое менеджер памяти java

Два потока имеют набор локальных переменных. 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-коде:

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

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

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

Каждый поток, выполняющий метод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, который является примитивным типом. Поскольку эти переменные являются переменными-членами, они все еще хранятся в куче вместе с объектом. В стеке потоков хранятся только локальные переменные.

Источник

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

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