Прочитав этот [JIT-компилятор], вы также можете сказать, что можете оптимизировать производительность Java!

Java
Прочитав этот [JIT-компилятор], вы также можете сказать, что можете оптимизировать производительность Java!

Привет всем, я Сяо Цай, Сяо Цай, который хочет быть Цай Буцаем в интернет-индустрии. Она может быть мягкой или жесткой, как она мягкая, а белая проституция жесткая!Черт~ Не забудьте поставить мне тройку после прочтения!

Эта статья в основном знакомитjava性能分析 之 JIT编译器

При необходимости вы можете обратиться к

Если это поможет, не забудьтекак

Творить нелегко, проституция бессмысленна!

Справочная литература: Полное руководство по производительности Java

Как разработчик Java, возможно, наиболее часто используемым в работе является простоCRUD,Устранение проблем с производительностьюМожет быть, не часто, но кое-что знать тоже нужно! Эта статья поможет вам изучитьJava中的JIT编译器.

Обзор предыдущей ситуации

немедленныйJIT(JUst-In-Time)Компилятор является ядром виртуальной машины Java.Производительность JVMНаибольшее влияние оказывает компилятор.

CPUЭто ядро ​​компьютера, и в данный момент он может выполнять лишь относительно небольшое количество специфических инструкций, таких какассемблерный кодибинарный код, поэтому все программы, выполняемые ЦП, должны быть переведены в такие инструкции.

语言结构
структура языка
  • Компилируемый язык: будет компилироваться вбинарная формаДля доставки сначала напишите программу, а затем используйте компилятор для статической генерации двоичного файла.
  • Интерпретируемый язык: один и тот же программный код может выполняться на любом процессоре, если на машине есть подходящий интерпретатор, и когда программа выполняется, интерпретатор преобразует соответствующий код в двоичный код.

Java пытается идти по среднему пути, и Java-приложения компилируются, но не в конкретный двоичный код, специфичный для процессора, а в идеализированный язык ассемблера. Этот язык ассемблера (байт-код Java) затем можно запустить в Java.. Итак, Java — этонезависимость от платформыизинтерпретируемый язык.

Подборка точек доступа

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

  • код — это код, который часто вызывается
  • Цикл, который выполняет много итераций

И эти критические фрагменты кода называютсяточка доступа приложения, чем больше код выполняется, тем он считаетсятем горячееиз. так что компилятор会先解释执行代码,然后找出哪个方法被调用的足够频繁,才进行编译. Это также для оптимизации: чем больше JVM выполняет определенный метод или цикл, тем лучше она понимает код, что позволяет JVM выполнять множество оптимизаций при компиляции кода.

резюме

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

Начало работы с настройкой

Один-два «аромата»

  • Клиентский компилятор
  • Компилятор сервера

При выборе типа компилятора в командной строке используются два вышеуказанных имени:-clientи-server. Обычно эти два компилятора также называютсякомпилятор c1 (клиентский компилятор)икомпилятор c2 (серверный компилятор)

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

Отключите многоуровневую компиляцию:java -client -XX:+TieredCompilation other_args

Основное различие между ними:

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

Вот проблема:

Может ли JVM начать с клиентского компилятора, а затем использовать серверный компилятор по мере того, как код нагревается?

план:

Многоуровневая компиляция: код сначала компилируется клиентским компилятором и перекомпилируется серверным компилятором по мере того, как код становится более горячим. В Java 1.8 многоуровневая компиляция включена по умолчанию.

2. Оптимизированный запуск

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

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

резюме:

  1. Компилятор клиента наиболее полезен, если время запуска приложения является основной проблемой производительности.
  2. Время запуска многоуровневой компиляции может быть очень близко ко времени запуска, полученному клиентским компилятором.

3. Оптимизируйте пакетную обработку

处理过程
обработка

Все сводится к тому, какой компилятор оптимизирует время выполнения приложения.

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

4. Оптимизируйте долго работающие приложения

Как правило, после того, как приложение «прогрелось», то есть проработало достаточно долго, чтобы был скомпилирован важный код, пришло время протестировать пропускную способность, с которой оно справляется. Результат тестирования приложения:

Время прогрева (секунды) - client - server -XX:+TieredCompilation
0 15.87 23.72 24.23
60 16.00 23.73 24.26
300 16.85 24.42 24.43

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

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

Промежуточная настройка компилятора

В большинстве случаев так называемая настройка компилятора на самом деле просто выбирает правильные параметры JVM и компилятора для Java на целевой машине (-client -server -XX:+TieredCompilation) Только. Многоуровневая компиляция обычно является лучшим выбором для приложений с длительным выполнением, а для приложений с коротким временем выполнения разница в производительности между многоуровневой компиляцией и клиентским компилятором минимальна.

1. Кэш кода

Когда JVM компилирует код, она сохраняет скомпилированный набор инструкций языка ассемблера в кэше кода. Размер кеша кода фиксирован, поэтому, как только он заполнится, JVM не сможет скомпилировать больше кода.Кэш кода слишком малЭто приведет к компиляции только некоторых горячих точек, а большая часть кода приложения просто интерпретируется и запускается —>运行慢

Когда кеш кода заполняется, JVM выдает следующее предупреждение:

JAVA HotSpot(TM) 64-Bit Server VM warning:CodeCache is full
     Compiler has been disabled
JAVA HotSpot(TM) 64-Bit Server VM warning:Try increasing the
     code cache size using -XX:ReservedCodeCacheSize=
Тип JVM Размер кэша кода по умолчанию
32-битный клиент, Java 8 32 MB
32-битный сервер, многоуровневая компиляция, Java 8 240 MB
64-битный сервер, многоуровневая компиляция, Java 8 240 MB
32-битный клиент, Java 7 32 MB
32-битный сервер, Java 7 32 MB
64-битный сервер, Java 7 48 MB
64-битный сервер, многоуровневая компиляция, Java 7 96 MB

Установите максимальное значение кеша кода

-XX:ReservedCodeCacheSize=N

Установите начальный размер кеша кода

-XX:InitialCodeCacheSize=N

резюме

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

  2. Многоуровневая компиляция может легко достичь верхнего предела конфигурации кэша кода по умолчанию (особенно в Java 7). При использовании многоуровневой компиляции следует контролировать кэш кода и при необходимости увеличивать его размер.

2. Порог компиляции

порог компиляции иКак часто выполняется кодВажно, что после выполнения кода определенное количество раз и достижения порога компиляции компилятор может получить достаточно информации для компиляции кода.

Компиляция основана на двухСчетчики JVM

  • счетчик вызовов методов
  • Счетчик возврата цикла в методе (обратную сторону можно рассматривать как количество раз, когда цикл завершил выполнение. Так называемое выполнение завершения цикла включает в себя достижение конца самого цикла, а также выполнение операторов ветвления, таких как continue)

1. 标准编译

Когда JVM выполняет метод Java, она проверяет общее количество двух счетчиков для метода, затем определяет, подходит ли метод для компиляции, и если да, то метод помещается в очередь компиляции.

Изменить порог компиляции

Зависит от-XX:CompileThreshold=NФлаг активирован, при использовании клиентского компилятора значение N по умолчанию равно1500, при использовании серверного компилятора значение N по умолчанию равно10 000,ИзменятьCompileThresholdзаставит компилятор ускорить (или задержать) компиляцию.

проблема:

Как посчитать, если цикл длинный или никогда не выходит?

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

2. 栈上编译 (OSR)

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

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

Это связано с тем, что хотя счетчики увеличиваются по мере выполнения методов и циклов, они также уменьшаются с течением времени.. Этот метод также называется温热方法

резюме:

  1. Компиляция происходит, когда выполнение методов и циклов достигает определенного порога.
  2. Изменение порога может привести к ранней или поздней компиляции кода.
  3. Поскольку счетчик со временем уменьшается, «теплый метод» может никогда не достичь порога компиляции (особенно для серверных компиляторов).

3. Процесс компиляции

Если мы хотим увидеть, как работает компилятор, мы можем использовать-XX:+PrintCompilationкоманда для включения, по умолчаниюfalse

Если этот флаг не установлен при запуске программы, вы можете использоватьjstatУзнайте о некоторых внутренних механизмах компилятора. Например:jstat -compiler 25илиjstat -printcompilation 25 1000

  • -compiler: предоставляет сводную информацию о том, сколько методов скомпилировано
  • -printcompilation: Получить последний скомпилированный метод
  • 25: идентификатор обнаруженного процесса
  • 1000: вывод каждую 1 секунду (1000 мс)

резюме:

  1. Лучший способ увидеть, как компилируется код, — включить PrintCompilation.
  2. Информация, выводимая при включении PrintCompilation, может использоваться для подтверждения того, что компиляция соответствует ожиданиям.

Расширенная настройка компилятора

Во-первых, поток компиляции

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

при использованииСтандартная компиляцияСкомпилированный метод, скомпилированный метод будет выполнен при следующем вызове метода; если вы используетеOSRСкомпилированный цикл, скомпилированный код будет выполнен на следующей итерации цикла.

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

использоватьclientПри компиляции JVM запустит поток компиляции; используйтеserverКогда компилятор работает, запускаются два потока. Многоуровневый компилятор зависит от немного более сложного уравнения, а именно:

Количество процессоров Количество потоков для C1 Количество потоков для C2
1 1 1
2 1 1
4 1 1
8 1 2
16 2 6
32 3 7
64 4 8
128 4 10

резюме:

  1. Компиляции методов, помещенных в очередь компиляции, выполняются асинхронно.
  2. Очереди не являются строго последовательными; горячие методы в очереди компилируются раньше других методов, что является еще одной причиной неправильного порядка идентификаторов в выходном журнале компиляции.

Во-вторых, встроенный в порядке?

понялfinalдрузья должны знать, чтоfinalУкрашенный метод, JVM попытается найти встроенный метод во время компиляции. Это потому, что самая важная оптимизация, которую делает компилятор,方法内联. Встроенное значение по умолчаниювключииз. в состоянии пройти-XX:-Inlineзакрытие.

Если вы скомпилируете JVM из исходного кода, вы можете использовать-XX:+PrintInlingСоздайте версию с отладочной информацией. ли методв линиюЗависит от того, насколько она горячая и насколько она велика. JVM определяет, является ли метод активным (например, часто вызывается) на основе внутренних вычислений; активный метод не имеет прямого отношения к каким-либо параметрам настройки.

резюме:

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

3. Анализ побега

мы можем пройти-XX:+DoEscapeAnalysisчтобы включить анализ побега, по умолчаниюtrue. Escape-анализ может определить, какие оптимизации возможны, а какие необходимы изменения в скомпилированном коде.

Escape-анализ включен по умолчанию, и в редких случаях он не работает. В таких случаях быстрее или стабильнее отключить его. Если вы столкнулись с такой ситуацией, лучший ответ — упростить соответствующий код: чем проще код, тем лучше.

резюме:

  1. Escape-анализ — самая сложная оптимизация, которую может выполнить компилятор, и такие оптимизации часто приводят к сбою микробенчмарков.
  2. Escape-анализ часто вводит неправильный код синхронизацииbug.

Что такое обратная оптимизация

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

  • Код сделан не входным
  • сделал зомби

1. Код сбрасывается?

Есть две причины, почему код отбрасывается

  • Связано с тем, как работают классы и интерфейсы
  • Связано с деталями многоуровневой компиляции

когдаserverПосле компиляции кода JVM должна заменитьclientКод компилируется компилятором. Это пометит брата Ама как устаревшего. Также замените только что скомпилированный (и более эффективный) код тем же методом.

2. Появляется код «Зомби»

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

С точки зрения производительности это хорошо. Мы упоминали вышекеш кода, скомпилированный код хранится в кэше кода фиксированного размера. Если обнаружен зомби-код, это означает, что проблемный код можно удалить из кеша кода, чтобы освободить место для другого кода, который будет скомпилирован (или ограничить объем памяти, которую JVM необходимо будет выделить позже).

Недостатком является то, что если код зомбирован, а затем снова загружен и используется часто, JVM необходимо перекомпилировать и повторно оптимизировать код, что повлияет на производительность.

резюме:

  1. Деоптимизация позволяет компилятору вернуться к скомпилированному коду предыдущей версии.
  2. Деоптимизация кода происходит, когда предыдущая оптимизация больше не действует (например, изменился тип задействованных объектов).
  3. Деоптимизация кода оказывает небольшое кратковременное влияние на производительность, но вновь скомпилированный код снова разогревается настолько быстро, насколько это возможно.
  4. При многоуровневой компиляции происходит деоптимизация, если код ранее был скомпилирован клиентским компилятором, а теперь оптимизируется серверным компилятором.

Многоуровневые уровни компиляции

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

вclientКомпилятор имеет3видовой уровень,serverКомпилятор имеет2Существуют различные уровни компиляции, поэтому уровни компиляции следующие:

  • 0: интерпретировать код
  • 1: Простой скомпилированный код C1
  • 2: Ограниченный скомпилированный код C1
  • 3: Полностью скомпилированный код C1
  • 4: Скомпилированный код C2

Большинство методов сначала компилируются на уровне 3., то есть полная компиляция C1 (хотя все методы начинаются с уровня 0). Если метод запускается достаточно часто, он компилируется до уровня 4 (код уровня 3 отбрасывается).

Если очередь компилятора сервера заполнена, метод берется из очереди сервера и компилируется на уровне 2, где компилятор C1 использует счетчики вызовов методов и счетчики обратной связи. Это ускорит компиляцию метода, и метод также будет скомпилирован на уровень 3 после того, как компилятор C1 соберет информацию о профилировании, и, наконец, на уровень 4, когда очередь серверного компилятора не будет слишком занята.

резюме:

  1. Возможна многоуровневая компиляция между двумя компиляторами и 5 уровнями.
  2. Не рекомендуется искусственно изменять уровень.

Гарниры и вы резюме

  1. Не беспокойтесь о маленьких методах, особенноgetterиsetter, так как они легко встраиваются.
  2. Код, который необходимо скомпилировать, находится в очереди на компиляцию, и чем больше кода в очереди, тем больше времени потребуется программе для достижения оптимальной производительности.
  3. Хотя размер кэша кода можно (и нужно) регулировать, он по-прежнему является ограниченным ресурсом.
  4. Чем проще код, тем больше оптимизаций, аналитическая обратная связь и анализ побегов могут сделать код быстрее, но сложные структуры циклов и большие методы ограничивают его эффективность.
看完不赞,就是坏蛋
Не понравилось после прочтения, все хреново

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

Если вы будете усердно работать сегодня, завтра вы сможете сказать на одну вещь меньше, чтобы попросить о помощи!