Как увидеть JIT-скомпилированный код в JVM?

Есть ли способ увидеть, что собственный код генерируется JIT в JVM?

Ответы

Ответ 1

Предполагая, что вы используете виртуальную виртуальную машину Sun Hotspot (то есть, предоставленную Oracle на java.com), вы можете добавить флаг

-XX:+PrintOptoAssembly

при запуске вашего кода. Это распечатает оптимизированный код, сгенерированный JIT-компилятором, и оставит остальные.

Если вы хотите увидеть весь байт-код, включая неоптимизированные части, добавьте

-XX:CompileThreshold=#

когда вы запускаете свой код.

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

Ответ 2

Общее использование

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

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Фильтр по определенному методу

Вы также можете фильтровать определенный метод со следующим синтаксисом:

-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod

Примечания:

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

Как установить необходимые библиотеки в Windows

Если вы используете Windows, эта страница содержит инструкции по сборке и установке hsdis-amd64.dll и hsdis-i386.dll, которые необходимы для работы. Мы скопируем ниже и расширим содержимое этой страницы * для справки:


Где получить готовые двоичные файлы

Вы можете загрузить готовые двоичные файлы для Windows из проекта fcml

Как создать hsdis-amd64.dll и hsdis-i386.dll в Windows

Эта версия руководства была подготовлена ​​на Windows 8.1 64bit с использованием 64-разрядного Cygwin и создала hsdis-amd64.dll

  • Установить Cygwin. На экране Select Packages добавьте следующие пакеты (расширив категорию Devel, затем щелкнув один раз на метке Skip рядом с каждым именем пакета):

    • make
    • mingw64-x86_64-gcc-core (требуется только hsdis-amd64.dll)
    • mingw64-i686-gcc-core (требуется только для hsdis-i386.dll)
    • diffutils (в категории Utils)
  • Запустите терминал Cygwin. Это можно сделать с помощью значка "Рабочий стол" или "Пуск", созданного установщиком, и по умолчанию создаст домашний каталог Cygwin (C:\cygwin\home\<username>\ или C:\cygwin64\home\<username>\).

  • Загрузите последний исходный пакет GNU binutils и извлеките его содержимое в домашний каталог Cygwin. На момент написания последнего пакета binutils-2.25.tar.bz2. Это должно привести к каталогу с именем binutils-2.25 (или любой другой последней версии) в вашем домашнем каталоге Cygwin.
  • Загрузите исходный код OpenJDK в репозиторий JDK 8 Updates, выбрав тег, соответствующий вашей установленной версии JRE, и нажмите bz2. Извлеките каталог hsdis (найденный в src\share\tools) в домашний каталог Cygwin.
  • В терминале Cygwin введите cd ~/hsdis.
  • Чтобы построить hsdis-amd64.dll, введите

    make OS=Linux MINGW=x86_64-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    Чтобы построить hsdis-i386.dll, введите

    make OS=Linux MINGW=i686-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    В любом случае замените 2.25 на загруженную версию binutils. OS=Linux необходимо, потому что, хотя Cygwin - это Linux-подобная среда, hsdis makefile не может распознать его как таковой.

  • Сбой сборки с сообщениями ./chew: No such file or directory и gcc: command not found. Измените <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile в текстовом редакторе, например Wordpad или Notepad ++, чтобы изменить SUBDIRS = doc po (строка 342, если использовать binutils 2.25) в SUBDIRS = po. Повторно запустите предыдущую команду.

Теперь DLL можно установить, скопировав его из hsdis\build\Linux-amd64 или hsdis\build\Linux-i586 в каталог JRE bin\server или bin\client. Вы можете найти все такие каталоги в своей системе, выполнив поиск java.dll.

Бонусный совет: если вы предпочитаете синтаксис Intel ASM для AT & T, укажите -XX:PrintAssemblyOptions=intel рядом с любыми другими параметрами PrintAssembly, которые вы используете.

* лицензия на страницу - это Creative Commons

Ответ 3

Для использования PrintAssembly вам нужен плагин hsdis. Удобный выбор - это плагин hsdis, основанный на библиотеке FCML.

Он может быть скомпилирован для UNIX-подобных систем, а в Windows вы можете использовать предварительно созданные библиотеки, доступные в разделе FCML download в Sourceforge:

Для установки в Windows:

  • Извлеките dll (его можно найти в hsdis-1.1.2-win32-i386.zip и hsdis-1.1.2-win32-amd64.zip).
  • Скопируйте dll туда, где есть java.dll (используйте поиск Windows). В моей системе я нашел это в двух местах:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Для установки в Linux:

  • Загрузите исходный код, извлеките его
  • cd <source code dir>
  • ./configure && make && sudo make install
  • cd example/hsdis && make && sudo make install
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/lib/amd64/hsdis-amd64.so
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/jre/lib/amd64/hsdis-amd64.so
  • В моей системе JDK находится в /usr/lib/jvm/java-8-oracle

Как запустить его:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly 
-XX:+LogCompilation -XX:PrintAssemblyOptions=intel,mpad=10,cpad=10,code 
-jar fcml-test.jar

Дополнительные параметры конфигурации:

code Код печатной машины перед мнемоникой.
intel Используйте синтаксис Intel.
gas Используйте синтаксис ассемблера AT & T (совместим с ассемблером GNU).
dec Печать IMM и смещение в виде десятичных значений.
mpad = XX Заполнение для мнемонической части инструкции.
cpad = XX Заполнение машинного кода.
Сег Показывает регистры сегментов по умолчанию.
нули Показывать ведущие нули в случае литералов HEX.

Синтаксис Intel по умолчанию используется в случае Windows, тогда как AT & T является стандартным для GNU/Linux.

Подробнее см. в Справочное руководство по библиотеке FCML

Ответ 5

Я считаю, что WinDbg будет полезен, если вы используете его на компьютере Windows. Я только что запустил одну банку.

  • Затем я подключился к процессу Java через Windbg
  • Проверенные потоки командой ~; Было 11 потоков, 0 поток был основным рабочим потоком
  • Переключено на 0-нить - ~ 0s
  • Посмотрел непрофессиональный столбец на kb:

    0008fba8 7c90e9c0 ntdll! KiFastSystemCallRet
    0008fbac 7c8025cb ntdll! ZwWaitForSingleObject + 0xc
    0008fc10 7c802532 kernel32! WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 kernel32! WaitForSingleObject + 0x12
    0008fc40 00402f68 java + 0x3a13
    0008fee4 004087b8 java + 0x2f68
    0008ffc0 7c816fd7 java + 0x87b8
    0008fff0 00000000 kernel32! BaseProcessStart + 0x23

Выделенные строки - это прямой код JIT-ed на JVM.

  • Затем мы можем искать адрес метода:
    java + 0x2f68 - 00402f68

  • На WinDBG:
    Нажмите "Просмотр" → "Разборка".
    Нажмите "Изменить" → "Перейти в адрес".
    Положите 00402f68 там
    и получил

    00402f68 55 push ebp
    00402f69 8bec mov ebp, esp
    00402f6b 81ec80020000 sub esp, 280h
    00402f71 53 push ebx
    00402f72 56 push esi
    00402f73 57 push edi
    ... и так далее

Для дополнительной информации здесь приведен пример, как отслеживать код JIT-ed из дампов памяти с помощью проводника процессов и WinDbg.

Ответ 6

Еще один способ увидеть машинный код и некоторые данные о производительности - использовать AMD CodeAnalyst или OProfile, у которых есть плагин Java, чтобы визуализировать исполняемый код Java как машинный код.

Ответ 7

Распечатайте сборку ваших горячих точек с помощью профилировщиков JMH (LinuxPerfAsmProfiler или WinPerfAsmProfiler). JMH требует библиотеки hsdis, поскольку она полагается на PrintAssembly.