предисловие
Если есть проблема с формулировкой или пониманием в тексте, пожалуйста, укажите на это. Эта статья направлена на то, чтобы упомянуть, не вдаваясь в подробности, но постараюсь максимально эффективно выбросить все точки знаний.
Во-первых, основное введение JVM
JVM — это аббревиатура Java Virtual Machine, это вымышленный компьютер, спецификация. Путем имитации различных компьютерных функций на реальном компьютере...
Ну, на самом деле, помимо такого профессионального предложения, мы знаем, что JVM на самом деле похожа на небольшой компьютер, работающий в среде операционной системы, такой как Windows или Linux. Он напрямую взаимодействует с операционной системой, а не с оборудованием, но операционная система может помочь нам завершить работу по взаимодействию с оборудованием.
1.1 Как выполняются файлы Java
Например, мы написали сейчас HelloWorld.java, потом этот HelloWorld.java в сторону, он похож на текстовый файл, но этот текстовый файл написан на английском языке и имеет определенные аббревиатуры Далее уже.
тогда нашJVMне знает о текстовых файлах, поэтому ему нуженкомпилировать, пусть это будет тот, который будет читать бинарные файлыHelloWorld.class
① Загрузчик классов
еслиJVMхочу выполнить это.classфайл, нам нужно загрузить его взагрузчик классов, он действует как портье и берет на себя всю.classВсе файлы перемещаются в JVM.
② Область метода
область методаОн используется для хранения данных, аналогичных информации метаданных, таких как информация о классе, константы, статические переменные, скомпилированный код и т. д.
Загрузчик классов перемещает файл .class и сначала бросает его на этот кусок.
③ куча
кучаВ основном он помещает некоторые сохраненные данные, такие как экземпляры объектов, массивы и т. д., которые принадлежат той же области методов, что и область методов.общая область потока. То есть онипоток небезопасениз
④ стек
кучаЭто время выполнения нашего кода. Каждый метод, который мы напишем, будет помещен вкучабеги внутрь.
Мы слышали о терминах "нативный стек методов" или "нативный интерфейс методов", но мы не будем раскрывать содержание этих двух частей. Нижний их слой использует для работы C, который не имеет ничего общего с Java.
⑤ Счетчик программ
Главное — выполнить задание загрузки, похожее на указатель, указывающий на следующую строку кода, которую нам нужно выполнить. Как стеки, обаЭксклюзивная темаДа, то есть у каждого потока будет своя соответствующая область без проблем параллелизма и многопоточности.
краткое содержание
- Файлы Java компилируются в файлы байт-кода .class.
- Файлы байт-кода перемещаются на виртуальную машину JVM через загрузчик классов.
- Пять основных блоков виртуальной машины: область методов и куча — это области с общим потоком, которые имеют проблемы с безопасностью потоков, стек и локальные стеки методов и счетчики — все это монопольные области, и проблем с безопасностью потоков нет. настройка JVM в основном вокруг кучи, а стек выполняется в двух блоках
1.2 Простой пример кода
простой студенческий класс
основной метод
Шаги для выполнения основного метода следующие:
- После компиляции App.java и получения App.class, выполнения App.class система запустит процесс JVM, найдет двоичный файл с именем App.class в пути к классам и загрузит информацию о классе App в область данных времени выполнения. область методов этого процесса называется загрузкой класса App
- JVM находит основную запись программы приложения и выполняет основной метод
- Первым оператором в этом main является Student student = new Student("tellUrDream"), что позволяет JVM создать объект Student, но в это время в области методов нет информации о классе Student, поэтому JVM сразу же загружает объект Student. Класс Student и puts Информация о классе Student размещается в области методов.
- После загрузки класса Student JVM выделяет память в куче для нового экземпляра Student, а затем вызывает конструктор для инициализации экземпляра Student, который содержитУказывает на информацию о типе класса Student в области методов.цитаты
- При выполнении student.sayName(); JVM находит объект учащегося в соответствии со ссылкой на учащегося, а затем находит таблицу методов информации о типе класса учащегося в области методов в соответствии со ссылкой, содержащейся в объекте учащегося, и получает адрес байт-кода sayName().
- выполнить скажи имя ()
На самом деле, не беспокойтесь слишком сильно, просто знайте, что когда экземпляр объекта инициализируется, он перейдет в область метода, чтобы найти информацию о классе, а затем перейдет в стек, чтобы запустить метод после завершения. Найдите метод в таблице методов.
Во-вторых, введение загрузчика классов
Ранее также упоминалось, что он отвечает за загрузку файлов .class.Они будут иметь определенную метку файла в начале файла, загружать содержимое байт-кода файла класса в память и преобразовывать это содержимое в структуру данных времени выполнения в область методов. , а ClassLoader отвечает только за загрузку файлов классов, а возможность его запуска определяется механизмом выполнения
2.1 Поток загрузчика классов
С момента загрузки класса в память виртуальной машины для освобождения памяти требуется всего 7 шагов: загрузка, проверка, подготовка, синтаксический анализ, инициализация, использование и выгрузка. вТри части проверки, подготовки и синтаксического анализа в совокупности называются соединением.
2.1.1 Загрузка
- Загрузите файл класса в память
- Преобразование статических структур данных в структуры данных времени выполнения в области методов
- Создайте объект java.lang.Class, представляющий этот класс в куче в качестве записи для доступа к данным.
2.1.2 Подключение
- Проверка: чтобы убедиться, что загруженный класс соответствует спецификациям и безопасности JVM, а также чтобы убедиться, что метод проверяемого класса не вызовет событий, которые подвергают опасности виртуальную машину во время выполнения.Фактически, это проверка безопасности.
- Подготовка: Выделите пространство памяти в области методов для статических переменных и установите начальное значение переменной, например, static int a = 3 (Примечание. На этапе подготовки в классе (в области методов) используются только статические переменные). set, исключая переменные экземпляра (память кучи)), переменные экземпляра назначаются при инициализации объекта)
- Анализ: виртуальная машина заменяет символическую ссылку в пуле констант прямой ссылкой (например, если я импортирую java.util.ArrayList, это символическая ссылка, а прямая ссылка — указатель или адрес объекта. Обратите внимание, что ссылочный объект должен быть в памяти)
2.1.3 Инициализация
Инициализация на самом деле является операцией присваивания, которая выполняет метод
2.1.4 Удаление
GC выгружает бесполезные объекты из памяти
2.2 Порядок загрузки загрузчиков классов
Порядок загрузки класса Class также является приоритетным, и порядок загрузчиков классов снизу вверх выглядит следующим образом.
- BootStrap ClassLoader: rt.jar
- Extention ClassLoader: загружает пакет jar расширения.
- App ClassLoader: пакет jar по указанному пути к классам
- Пользовательский ClassLoader: пользовательский загрузчик классов
2.3 Механизм родительского делегирования
Когда класс получает запрос на загрузку, он не будет пытаться загрузить его сам, а делегирует его для выполнения родительскому классу. Например, я хочу создать нового человека сейчас. Этот человек — наш пользовательский класс. Если мы хотите загрузить Он сначала делегирует App ClassLoader. Только когда загрузчик родительского класса сообщает, что он не может выполнить запрос (то есть загрузчик родительского класса не находит класс, необходимый для загрузки), загрузчик дочернего класса попытается загрузить это само по себе
Преимущество этого заключается в том, что независимо от того, какой загрузчик загружает классы, расположенные в пакете rt.jar, в конечном итоге он будет делегирован для загрузки загрузчику классов BootStrap, что гарантирует получение одного и того же результата при использовании разных загрузчиков классов.
По сути, это еще и функция изоляции, которая предотвращает влияние нашего кода на код JDK.
public class String(){
public static void main(){sout;}
}
В этот раз наш код обязательно сообщит об ошибке, потому что при загрузке он фактически находит String.class в rt.jar, а потом находит, что нет основного метода.
3. Область рабочих данных
3.1 Стек собственных методов и счетчик программ
Например, если мы сейчас нажмем на исходный код класса Thread, мы увидим, что его метод start0 изменен с помощью ключевого слова native, а тело метода отсутствует.Этот метод, модифицированный с помощью native, является локальным методом, который реализован с помощью C. , а затем, как правило, эти методы помещаются в область, называемую стеком собственных методов.
Счетчик программ на самом деле является указателем, который указывает на следующую инструкцию в нашей программе, которую необходимо выполнить, а также это единственная область в области памяти, где OutOfMemoryError не произойдет, а занимаемое место в памяти настолько мало, что может быть проигнорировано. Эта память представляет собой только индикатор номера строки байт-кода, выполняемого текущим потоком, и анализатор байт-кода выбирает следующую инструкцию байт-кода для выполнения, изменяя значение этого счетчика.
Если реализация является нативным методом, то этот указатель работать не будет.
3.2 Область метода
Основная функция области методов — хранить информацию о метаданных класса, константы и статические переменные и т. д. Когда информация, которую он хранит, слишком велика, он сообщит об ошибке, когда выделение памяти не может быть удовлетворено.
3.3 Стек виртуальных машин и куча виртуальных машин
В одном предложении: работа со стопкой труб, хранение кучи труб. Стек виртуальной машины отвечает за выполнение кода, а куча виртуальной машины отвечает за хранение данных.
3.3.1 Концепция стека виртуальных машин
Это модель памяти для выполнения методов Java. Он будет хранить локальные переменные, динамические связанные списки, выходы из методов и операции со стеком (вталкивание и извлечение), которые используются исключительно потоками. В то же время, если мы слышим таблицу локальных переменных, это также говорит о стеке виртуальной машины.
public class Person{
int a = 1;
public void doSomething(){
int b = 2;
}
}
3.3.2 Исключения в стеке виртуальных машин
Если глубина стека, запрошенная потоком, больше максимальной глубины стека виртуальной машины, он сообщитStackOverflowError(Ошибка такого рода часто возникает при рекурсии). Виртуальная машина Java также может быть динамически расширена, но поскольку расширение будет продолжать применяться к памяти, она сообщит об ошибке, когда не сможет применить достаточно памяти.OutOfMemoryError.
3.3.3 Жизненный цикл стека виртуальных машин
Для стека нет сборки мусора. Как только программа завершит работу, пространство стека, естественно, будет освобождено. Жизненный цикл стека соответствует потоку, в котором он находится.
Вот дополнительное предложение: 8 основных типов переменных + переменные ссылки на объект + методы экземпляра — все это выделенная память в стеке.
3.3.4 Выполнение стека виртуальной машины
Данные фрейма стека, о которых мы часто говорим, в JVM называются фреймом стека.Помещение их в Java на самом деле является методом, и они также хранятся в стеке.
Данные в стеке существуют в формате кадра стека, который представляет собой набор данных о методах и данных времени выполнения. Например, когда мы выполняем метод a, соответственно будет сгенерирован кадр стека A1, а затем A1 будет помещен в стек. Точно так же метод b будет иметь B1, а метод c будет иметь C1.После выполнения потока в стеке сначала появится C1, затем B1 и A1. Это принцип FIFO, LIFO.
3.3.5 Повторное использование локальных переменных
Таблица локальных переменных используется для хранения параметров метода и локальных переменных, определенных внутри метода. Его емкость является наименьшей единицей слота, и слот может хранить типы данных в пределах 32 бит.
Виртуальная машина использует таблицу локальных переменных путем позиционирования индекса, а диапазон равен [0, количество слотов в таблице локальных переменных]. Параметры в методе будут располагаться в этой таблице локальных переменных в определенном порядке, и нам может быть все равно, как они расположены. Для экономии места в кадре стека эти слоты можно использовать повторно.Когда позиция выполнения метода превышает определенную переменную, слот этой переменной может быть повторно использован другими переменными. Конечно, если нам нужно переиспользовать, то наша сборка мусора естественно не будет перемещать эту память.
3.3.6 Концепция кучи виртуальной машины
Память JVM делится на память кучи и память без кучи, а память кучи также делится намолодое поколениеистарость, а память без кучипостоянное поколение. Молодое поколение будет разделено наEdenиSurvivorПлощадь. Выживший также делится наFromPlaceиToPlace, область выживания toPlace пуста. Соотношение Eden, FromPlace и ToPlace по умолчанию равно8:1:1. Конечно, эту вещь также можно динамически настроить в соответствии со скоростью генерируемого объекта с помощью параметра -XX:+UsePSAdaptiveSurvivorSizePolicy.
Объекты хранятся в куче памяти, а сборка мусора состоит в том, чтобы собрать эти объекты и передать их алгоритму GC для переработки. На самом деле, мы уже говорили, что не куча памяти — это область методов. Постоянное поколение было удалено в 1.8, а замена - это метапространство (MetaSpace).Самое большое отличие состоит в том, что метапространство не существует в JVM, оно использует локальную память. и имеет два параметра
MetaspaceSize:初始化元空间大小,控制发生GC
MaxMetaspaceSize:限制元空间大小上限,防止占用过多物理内存。
Причину удаления можно примерно понять: изменения, сделанные путем интеграции HotSpot JVM и JRockit VM, потому что у JRockit нет постоянной генерации, но это также косвенно решает проблему OOM постоянной генерации.
3.3.7 Введение в Eden Young Generation
Когда мы создаем новый объект, мы сначала помещаем его в часть памяти, разделенную Eden в качестве пространства для хранения, но мы знаем, что память кучи совместно используется потоками, поэтому может случиться так, что два объекта совместно используют память. Обработка JVM здесь заключается в том, что каждый поток будет претендовать на непрерывное пространство памяти заранее и указать место хранения объекта, а если места недостаточно, он будет претендовать на несколько пространств памяти. Мы назовем эту операцию TLAB, если вам интересно, вы можете узнать о ней.
Когда пространство Эдема заполнится, будет запущена операция под названием Minor GC (то есть GC, происходящая в молодом поколении), и уцелевшие объекты будут перемещены в область Survivor0. После того, как область Survivor0 заполнена, срабатывает Minor GC, и выживший объект будет перемещен в область Survivor 1. В это время будут обменены указатели from и to, что гарантирует, что всегда будет область выжившего, которая пусто, а выживший, на которого в течение некоторого времени указывает на. Область пуста. Объекты, пережившие несколько незначительных GC (Оценка выживания здесь составляет 15 раз, что соответствует параметру виртуальной машины -XX:MaxTenuringThreshold. Почему 15, потому что HotSpot будет записывать возраст в поле тега приведения объекта, а выделенное пространство составляет всего 4 бита, поэтому он может записывать только до 15) перейдет к старому поколению. Старое поколение хранит долгоживущие объекты.Когда оно заполнится, оно запустит наиболее часто слышимую полную сборку мусора.В этот период все потоки будут остановлены и будут ждать завершения сборки мусора. Следовательно, для приложений с высокими требованиями к ответу следует свести к минимуму возникновение полного GC, чтобы избежать проблемы тайм-аута ответа.
Более того, когда старая область все еще не может выполнить операцию сохранения объекта после выполнения полного gc, произойдет OOM.В это время памяти кучи в виртуальной машине недостаточно.Причина может заключаться в том, что размер настройки памяти кучи слишком мал.Это можно сделать через параметр -Xms, -Xmx настроить. Также может быть, что объекты, созданные в коде, большие и многочисленные, и на них постоянно ссылаются, так что долговременная сборка мусора не может их собрать.
3.3.8 Как определить, нужно ли уничтожать объект
На рисунке счетчик программ, стек виртуальной машины и локальный стек методов — три области выживают вместе с выживанием потока. Выделение и освобождение памяти являются детерминированными. По мере завершения потока память, естественно, освобождается, поэтому нет необходимости рассматривать проблему сборки мусора. Куча Java отличается от области методов.Каждый поток является общим, а выделение и повторное использование памяти являются динамическими. Таким образом, сборщик мусора занимается кучей и частью памяти методов.
Перед утилизацией необходимо определить, какие объекты еще живы, а какие мертвы. Ниже описаны два основных метода расчета.
1. Вычисление счетчика ссылок: добавьте счетчик ссылок к объекту.Каждый раз, когда на объект ссылаются, счетчик увеличивается на единицу, а ссылка уменьшается на единицу по истечении срока действия ссылки.Когда счетчик равен 0, он будет не использоваться повторно. Однако в этом методе возникает ситуация, когда сборщик мусора не может восстановиться при наличии циклической ссылки на объект.
2. Анализ и расчет достижимости: Это реализация, аналогичная бинарному дереву. В качестве начального набора уцелевших объектов используется ряд GC ROOTS. От этого узла осуществляется поиск вниз, и путь, пройденный поиском, становится цепочкой ссылок Объекты, на которые может ссылаться эта коллекция, добавляются в коллекцию. Поиск Когда объект не использует какую-либо цепочку ссылок на корни GC, объект недоступен. Основные коммерческие языки программирования, такие как Java, C# и т. д., полагаются на этот трюк, чтобы определить, жив ли объект.
(Просто знайте об этом) Объекты, которые можно использовать в качестве GC Roots в языке Java, делятся на следующие категории:
- Объекты (локальные переменные), на которые ссылается стек виртуальной машины (таблица локальных методов во фрейме стека)
- Объект, на который ссылается статическая переменная в области метода (статическая переменная)
- Объект, на который ссылается константа в области метода
- Объект, на который ссылается JNI в локальном стеке методов (то есть метод, модифицированный нативным) (JNI — это способ, которым виртуальная машина Java вызывает соответствующую функцию C, и новый объект Java также может быть создан с помощью функции JNI. И у JNI есть локальная или глобальная ссылка на объект, он пометит объекты, на которые они указывают, как не подлежащие повторному использованию)
- Запущенный и незавершенный поток Java
Преимущество этого метода в том, что он может решить проблему циклических ссылок, но его реализация требует много ресурсов и времени, а также требует GC (его ссылка процесса анализа не может измениться, поэтому все процессы должны быть остановлены)
3.3.9 Как объявить истинную смерть объекта
Первое, что следует упомянуть, этоfinalize()Методы
finalize() — это метод класса Object. Метод finalize() объекта будет автоматически вызываться системой только один раз. После того, как метод finalize() выйдет из мертвого объекта, он не будет вызываться снова во второй раз .
Дополнение: Не рекомендуется вызывать finalize() в программе для самоспасения. Рекомендуется забыть о существовании этого метода в программах на Java. Поскольку время его выполнения является неопределенным, даже то, выполняется ли оно, является неопределенным (аномальный выход программы Java), а эксплуатационные расходы высоки, а порядок вызова каждого объекта не может быть гарантирован (даже в разных потоках). В Java9 был помечен какdeprecated, и он был постепенно заменен на java.lang.ref.Cleaner (то есть набор сильных, мягких, слабых и фантомных ссылок), который будет более легким и надежным, чем finalize.
Для определения гибели объекта требуется не менее двух отметок
- Если объект не находит цепочку ссылок, связанную с GC Roots после анализа достижимости, он будет помечен и экранирован в первый раз. Условие решения состоит в том, чтобы решить, нужно ли выполнять метод finalize() для этого объекта. Если объекту необходимо выполнить метод finalize(), он помещается в очередь F-Queue.
- GC дважды помечает объекты в очереди F-Queue. Если объект повторно связан с любым объектом в цепочке ссылок в методе finalize(), он будет удален из коллекции «для переработки», когда будет помечен во второй раз. Если в это время объект не сбежал успешно, его можно только переработать.
Если объект определен как мертвый, как мы можем переработать мусор?
3.4 Алгоритм сборки мусора
Он не будет подробно раскрываться.Обычно используемые алгоритмы - это удаление тегов, репликация, сортировка тегов и сбор по поколениям.
3.4.1 Алгоритм развертки пометки
Алгоритм маркировки-подметания делится на две фазы: «маркировка» и «подметание». Отметьте все объекты, которые необходимо переработать, и равномерно переработайте их после окончания маркировки. Эта процедура очень проста, но имеет и недостатки, на основе которых строятся последующие алгоритмы, требующие усовершенствования.
Фактически он помечает мертвый объект как свободную память, а затем записывает его в список свободных.Когда нам нужно создать новый объект, модуль управления памятью найдет свободную память из списка свободных для выделения новому объекту.
Недостатком является относительно низкая эффективность маркировки и очистки. И такой подход вызовет большую фрагментацию в памяти. Это приводит к невозможности выделить достаточно непрерывной памяти, если нам нужно использовать большие блоки памяти. Например, следующий рисунок
Все блоки памяти, которые можно использовать в это время, разбросаны, что приводит к только что упомянутой проблеме больших объектов памяти.
3.4.2 Алгоритм репликации
Для решения проблемы эффективности появился алгоритм репликации. Он делит доступную память на две равные части по емкости, используя только один блок за раз. Как и в режимеSurvival, он также использует два указателя from и to для воспроизведения. Когда fromPlace заполнится, скопируйте уцелевший объект в другой toPlace, а затем замените содержимое указателя. Это решает проблему фрагментации.
Цена этого алгоритма заключается в уменьшении памяти, поэтому эффективность использования памяти кучи станет очень низкой.
Однако, когда они распределяются, они не распределяются в соответствии с 1:1, точно так же, как Eden и Survivor не являются эквивалентными распределениями.
3.4.3 Алгоритм сопоставления меток
Алгоритм репликации имеет определенные проблемы с эффективностью, когда процент выживаемости объектов высок.Процесс маркировки такой же, как и в алгоритме «маркировка-очистка», но последующие шаги заключаются не в непосредственной очистке перерабатываемых объектов, а в том, чтобы сделать все выжившие объекты уходят в один конец.двигаются, а потом прямо память за грань подчищают
3.4.4 Алгоритм сбора поколений
В этом алгоритме нет никаких новых идей, но он делит память на несколько блоков в соответствии с жизненным циклом объекта. Как правило, куча Java делится на новое поколение и старое поколение, чтобы можно было выбрать наиболее подходящий алгоритм сбора в соответствии с характеристиками каждого поколения. В новом поколении обнаруживается, что большое количество объектов умирает во время каждой сборки мусора, и лишь немногие выживают, поэтому используется алгоритм репликации, и сбор может быть завершен лишь с небольшими затратами на копирование уцелевших объектов. В старости, поскольку вероятность выживания объекта высока и нет дополнительного места для его выделения, для утилизации необходимо использовать алгоритм «отметить-очистить» или «отметить-очистить».
Проще говоря, Восемь Бессмертных пересекают море и демонстрируют свои магические силы, а конкретные проблемы подробно разбираются.
3.5 (Понимание) Различные сборщики мусора
Сборщик мусора в HotSpot VM и применимые сценарии
Начиная с jdk8 сборщиками мусора по умолчанию являются Parallel Scavenge и Parallel Old.
Начиная с jdk9, сборщик мусора G1 становится сборщиком мусора по умолчанию.
На данный момент сборщик G1 имеет наименьшее время паузы и не имеет явных недостатков, что очень подходит для веб-приложений. При тестировании веб-приложения в jdk8 куча памяти составляет 6 ГБ, а новое поколение — 4,5 ГБ. Parallel Scavenge делает паузу до 1,5 секунд для восстановления нового поколения. Сборщик G1 останавливается всего на 0,2 секунды, чтобы собрать молодое поколение того же размера.
3.6 (понять) общие параметры JVM
Параметров JVM много, вот лишь несколько наиболее важных, которые также можно получить через различные поисковые системы.
имя параметра | значение | По умолчанию | инструкция |
---|---|---|---|
-Xms | начальный размер кучи | 1/64 физической памяти ( | По умолчанию (параметр MinHeapFreeRatio можно настроить), когда свободная память кучи меньше 40%, JVM будет увеличивать кучу до максимального предела -Xmx. |
-Xmx | максимальный размер кучи | 1/4 физической памяти ( | По умолчанию (параметр MaxHeapFreeRatio можно настроить), когда свободная память кучи больше 70%, JVM уменьшит кучу до минимального предела -Xms |
-Xmn | Размер молодого поколения (1.4 или позже) | Примечание: Размер здесь (eden + 2 пространства для выживших) отличается от нового поколения, показанного в jmap -heap. Размер всей кучи = размер молодого поколения + размер старого поколения + размер постоянного поколения (постоянного поколения).После увеличения молодого поколения размер старого поколения будет уменьшен.Это значение оказывает большое влияние на производительность системы.Sun официально рекомендует что все 3/8 кучи | |
-XX:NewSize | Установить размер молодого поколения (для 1.3/1.4) | ||
-XX:MaxNewSize | Максимум молодого поколения (для 1.3/1.4) | ||
-XX:PermSize | Установите начальное значение постоянной генерации (perm gen) | 1/64 физической памяти | |
-XX:MaxPermSize | Установить максимальное постоянное поколение | 1/4 физической памяти | |
-Xss | размер стека на поток | После JDK5.0 размер стека каждого потока составляет 1 М. Ранее размер стека каждого потока составлял 256 КБ. Размер памяти, необходимый для большего количества потоков приложений, можно регулировать. При той же физической памяти уменьшение этого значения может привести к созданию большего количества потоков. .Однако операционная система по-прежнему имеет ограничение на количество потоков в процессе, которое не может генерироваться бесконечно.Значение опыта обычно составляет около 3000~5000 для небольших приложений.Если стек не очень глубокий, 128 КБ должно быть достаточно. для больших приложений рекомендуется использовать 256k. Этот параметр сильно влияет на производительность и требует тщательного тестирования. (Принципиальный) Объяснение параметра threadstacksize очень похоже. Официальный документ вроде не поясняет. На форуме есть такое предложение: -Xss переводится в флаг ВМ с именем ThreadStackSize" Вообще это значение можно ставить . | |
-XX:NewRatio | Отношение молодого поколения (включая Эдем и два региона Выживших) к старому поколению (исключая постоянное поколение) | -XX:NewRatio=4 означает, что соотношение между молодым поколением и старым поколением составляет 1:4, и молодое поколение занимает 1/5 всего стека, когда Xms=Xmx и Xmn установлены, этот параметр не нужно быть установлен. | |
-XX:SurvivorRatio | Отношение размера области Эдема к площади Выжившего | Если установлено значение 8, соотношение двух областей Выживших к одной области Эдема составляет 2:8, а на одну область Выживших приходится 1/10 всего молодого поколения. | |
-XX:+DisableExplicitGC | закрыть System.gc() | Этот параметр требует тщательного тестирования | |
-XX:PretenureSizeThreshold | Объекты по размеру выделяются непосредственно в старом поколении | 0 | Unit byte Новое поколение недопустимо при использовании Parallel ScavengeGC Другая ситуация, которая прямо выделяется в старом поколении, — это большой объект массива, и в массиве нет объекта внешней ссылки. |
-XX:ParallelGCThreads | Количество потоков для параллельных коллекторов | Это значение лучше всего настроить равным количеству процессоров. Также относится к CMS. | |
-XX:MaxGCPauseMillis | Максимальное время для каждой сборки мусора молодого поколения (максимальное время паузы) | Если это время не может быть соблюдено, JVM автоматически отрегулирует размер молодого поколения в соответствии с этим значением. |
На самом деле есть некоторые параметры печати и CMS, которые здесь не указаны.
В-четвертых, некоторые аспекты настройки JVM
В соответствии с знаниями только что задействованной jvm, мы можем попытаться настроить JVM, в основном память кучи
Все потоки совместно используют размер области данных = размер молодого поколения + размер старого поколения + размер постоянного поколения. Постоянная генерация обычно имеет фиксированный размер 64 м. Следовательно, после увеличения молодого поколения в куче java размер старого поколения будет уменьшен (т.к. старое поколение очищается с помощью fullgc, если старое поколение слишком маленькое, оно увеличит fullgc). Это значение сильно влияет на производительность системы, и Sun официально рекомендует, чтобы оно составляло 3/8 кучи Java.
4.1 Настройка максимальной и минимальной памяти кучи
-Xmx –Xms: укажите максимальную кучу Java (по умолчанию 1/4 физической памяти (
Когда по умолчанию (параметр MinHeapFreeRatio можно настроить) свободная память кучи меньше 40%, JVM будет увеличивать кучу до максимального предела -Xmx.Когда по умолчанию (параметр MaxHeapFreeRatio можно настроить) свободная память кучи больше 70 %, JVM уменьшит кучу до минимального предела для Xms. Проще говоря, вы продолжаете забрасывать данные в память кучи.Когда ее оставшийся размер меньше 40%, JVM будет динамически запрашивать место в памяти, но оно будет меньше -Xmx.Если оставшийся размер больше 70 %, он будет динамически уменьшаться, будет не меньше –Xms. Это так просто
В процессе разработки два параметра -Xms и -Xmx обычно настраиваются с одинаковыми значениями.Цель состоит в том, чтобы тратить ресурсы без необходимости повторного расчета размера области кучи после того, как механизм сборки мусора Java очистит область кучи. .
Мы выполняем следующий код
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间
Примечание. Здесь задается размер кучи Java, то есть размер нового поколения + размер старого поколения
Установите параметр опций виртуальной машины
-Xmx20m -Xms5m -XX:+PrintGCDetails
Запустите основной метод снова
Здесь сборщик мусора выдает сообщение об ошибке распределения, которое происходит в PSYoungGen, то есть в молодом поколении.
В настоящее время применяемая память составляет 18 МБ, а свободная память — 4,214195251464844 МБ.
Давайте создадим массив байтов в этой точке и выполним следующий код
byte[] b = new byte[1 * 1024 * 1024];
System.out.println("分配了1M空间给数组");
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
В это время свободная память снова сократилась, но общая память не изменилась. Java попытается сохранить значение total mem до минимального размера кучи памяти.
byte[] b = new byte[10 * 1024 * 1024];
System.out.println("分配了10M空间给数组");
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间
В это время мы создали 10M байт данных, и минимальная куча памяти в это время невыносима. Мы обнаружим, что текущая общая память стала 15 МБ, что является результатом того, что мы подали заявку на память один раз.
Теперь давайте снова запустим этот код
System.gc();
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系统的最大空间
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系统的空闲空间
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //当前可用的总空间
В это время мы вручную выполнили полный gc, и объем памяти от общей памяти снова изменился до 5,5 М. На этот раз это было результатом освобождения запрошенной памяти.
4.2 Корректировка соотношения молодого поколения к старшему поколению
-XX:NewRatio --- соотношение молодого поколения (eden+2*Survivor) к старому поколению (исключая постоянную область)
Например: -XX:NewRatio=4, что указывает на то, что новое поколение: старое поколение=1:4, то есть новое поколение занимает 1/5 всей кучи. Когда установлено Xms=Xmx и Xmn, этот параметр не нужно устанавливать.
4.3 Отрегулируйте соотношение области Survivor и Eden
-XX:SurvivorRatio (поколение выживших) --- установить соотношение двух областей выживших к eden
Например: 8 означает двух Выживших:eden=2:8, то есть на одного Выжившего приходится 1/10 молодого поколения.
4.4 Установить размер молодого и старого поколений
-XX:NewSize --- установить размер молодого поколения
-XX:MaxNewSize --- установить максимальное значение молодого поколения
Вы можете тестировать разные ситуации, задавая разные параметры.В любом случае, оптимальное решение, конечно, что официальное соотношение Эдема и Выжившего 8:1:1, а то какие-то инструкции были приложены, когда эти параметры только вводились. . В любом случае, если максимальная память кучи и минимальная память кучи различаются, это вызовет несколько gcs, поэтому вам нужно обратить внимание.
4.5 Краткое резюме
Отрегулируйте размер молодого поколения и выжившего поколения в соответствии с реальной ситуацией.Официально рекомендуется, чтобы молодое поколение составляло 3/8 кучи Java, а выжившее поколение должно составлять 1/10 нового поколения. .
Во время OOM не забудьте создать дамп кучи, чтобы убедиться, что вы можете устранять неполадки на месте.С помощью следующей команды вы можете вывести файл .dump.Этот файл можно использовать с помощью VisualVM или инструмента Java VisualVM, поставляемого с Java.
-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=你要输出的日志路径
Как правило, мы также можем написать скрипт, который позволит нам сообщать о письме при появлении OOM, что можно решить, отправив электронное письмо или перезапустив программу.
4.6 Настройка постоянной области
-XX:PermSize -XX:MaxPermSize
Начальное пространство (по умолчанию — 1/64 физической памяти) и максимальное пространство (по умолчанию — 1/4 физической памяти). То есть при запуске JVM постоянная область занимает пространство размера PermSize с самого начала, если места недостаточно, она может продолжать расширяться, но не может превышать MaxPermSize, иначе будет OOM.
Советы: Если пространство кучи не используется и выбрасывается OOM, это может быть вызвано постоянной областью. Пространство кучи на самом деле занято очень мало, но переполнение постоянной области также вызывает OOM.
4.7 Настройка параметров стека JVM
4.7.1 Настройка размера пространства стека каждого потока
Вы можете настроить размер пространства стека каждого потока с помощью -Xss:
После JDK5.0 размер стека каждого потока составляет 1 МБ, а предыдущий размер стека каждого потока — 256 КБ. В той же физической памяти уменьшение этого значения может создать больше потоков. Однако операционная система по-прежнему имеет ограничение на количество потоков в процессе, которое не может генерироваться бесконечно.Значение опыта составляет около 3000~5000.
4.7.2 Установка размера стека потоков
-XXThreadStackSize:
设置线程栈的大小(0 means use default stack size)
Эти параметры можно просто проверить, написав программы самостоятельно.Из-за нехватки места демо-версии здесь больше не предоставляются.
4.8 (Можно сразу пропустить) Знакомство с другими параметрами JVM
Параметров всяких очень много, так что я не буду говорить об этом всём, потому что люди на самом деле не говорят, что надо докапываться до сути.
4.8.1 Установка размера страниц памяти
-XXThreadStackSize:
设置内存页的大小,不可设置过大,会影响Perm的大小
4.8.2 Настройка быстрой оптимизации для примитивных типов
-XX:+UseFastAccessorMethods:
设置原始类型的快速优化
4.8.3 Отключение ручного ГХ
-XX:+DisableExplicitGC:
设置关闭System.gc()(这个参数需要严格的测试)
4.8.4 Установка максимального возраста мусора
-XX:MaxTenuringThreshold
设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.
对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,
则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,
增加在年轻代即被回收的概率。该参数只有在串行GC时才有效.
4.8.5 Ускорение компиляции
-XX:+AggressiveOpts
ускорить компиляцию
4.8.6 Улучшение работы механизма блокировки
-XX:+UseBiasedLocking
4.8.7 Отключение сборки мусора
-Xnoclassgc
4.8.8 Установка времени выживания пространства кучи
-XX:SoftRefLRUPolicyMSPerMB
设置每兆堆空闲空间中SoftReference的存活时间,默认值是1s。
4.8.9 Установка объектов, непосредственно размещенных в старом поколении
-XX:PretenureSizeThreshold
设置对象超过多大时直接在老年代分配,默认值是0。
4.8.10 Установить долю TLAB в области eden
-XX:TLABWasteTargetPercent
设置TLAB占eden区的百分比,默认值是1% 。
4.8.11 Укажите, следует ли отдавать приоритет YGC
-XX:+CollectGen0First
设置FullGC时是否先YGC,默认值是false。
finally
Я давно об этом говорю, ссылался на разные материалы, в том числе на Geek Time «Углубленный разбор виртуальных машин» и «Интервью по основным технологиям Java», а также на Baidu и некоторые онлайн-курсы, которые я изучаю. Резюме. Надеюсь, это поможет вам, спасибо.
Сейчас я управляю собственной планетой знаний, она бесплатная, но это не значит, что нет никакой выгоды. Студенты, интересующиеся направлением больших данных, могут обратить внимание на