Як побачити JIT-компільований код у JVM?


84

Чи є спосіб побачити власний код, який виробляє JIT, у JVM?


Ви впевнені, що хочете побачити компільований JIT (власний) код або просто байт-код? Я запитую, тому що задання цього питання тут призводить до певних сумнівів, якщо ви дійсно хочете побачити власний код ... І, вибачте, я теж не знаю такого інструменту.
— gimpf

3
Я хочу бачити вбудований код, скомпільований JIT. Звичайно, це не те, що мені потрібно, щоб зробити роботу, а різновид експериментів та досліджень.
— alsor.net

Невеликий виклик кадру: динамічний компілятор, який використовується в сучасних JVM, не має лише однієї версії скомпільованого коду; він може розпочати інтерпретацію, потім скомпілювати метод або лише частину його, а потім потенційно повторно скомпілювати його кілька разів, коли класи завантажуються / розвантажуються або зміна шаблонів використання або на основі статистики продуктивності. (Я думаю, це може навіть відкинути скомпільовану версію і повернутися до інтерпретації, якщо це здається вигідним.) Отже, ви можете отримати не тільки різний код на різних машинах, навіть для різних запусків на одній машині, але в різний час в одному запуску .
— gidds

Відповіді:


45

Припускаючи, що ви використовуєте Sun Hotspot JVM (тобто той, який надає Oracle на java.com ), ви можете додати прапор

-XX: + PrintOptoAssembly

під час запуску коду. Це роздрукує оптимізований код, створений компілятором JIT, а решту залишить.

Якщо ви хочете побачити весь байт-код, включаючи неоптимізовані частини, додайте

-XX: CompileThreshold = #

коли ви запускаєте свій код.

Детальніше про цю команду та функціональність JIT ви можете прочитати тут .


Цей параметр присутній лише у збірках налагодження чи щось інше? Оскільки мій JVM ("Java (TM) SE Runtime Environment (збірка 1.6.0_16-b01") не розпізнає його, навіть якщо джерело в Інтернеті вказує, що ця функція доступна в Sun Java 6 та OpenJDK.
— Йоахім Зауер,

2
Так, потрібні двійкові файли DEBUG. blogs.warwick.ac.uk/richardwarburton/entry/…
— alsor.net

3
Хіба це не повинно бути (сьогодні) -XX: + PrintAssembly, принаймні сьогодні? Перевірено на моїй машині та відповідає сказаному тут: wikis.sun.com/display/HotSpotInternals/PrintAssembly Вам потрібна -XX: + UnlockDiagnosticVMOptions перед цією опцією та плагін для розбірки.
— Blaisorblade

@Blaisorblade Я отримую: Неправильно вказаний параметр VM Помилка "PrintAssembly": Не вдалося створити віртуальну машину Java. Помилка: сталося фатальне виняток. Програма вийде.
— Корай Тугай

@KorayTugay Див. Інші відповіді - оновленим посиланням є wikis.oracle.com/display/HotSpotInternals/PrintAssembly , як зазначено у stackoverflow.com/a/15146962/53974 або stackoverflow.com/a/4149878/53974 . Якщо наступні вказівки не працюють, будь ласка, запитайте деталі у певному відповідному місці (не впевнені, чи слід це ще одне запитання для вашої справи, посилаючись на це).
— Blaisorblade

76

Загальне використання

Як пояснюється іншими відповідями, ви можете запустити такі опції 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

  1. Встановіть Cygwin . На Select Packagesекрані додайте наступні пакети (розширивши Develкатегорію, а потім клацнувши один раз на Skipмітку біля кожного імені пакета):

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

  3. Завантажте найновіший вихідний пакет GNU binutils та витягніть його вміст до домашнього каталогу Cygwin. На момент написання останньої упаковки є binutils-2.25.tar.bz2. Це має призвести до створення каталогу з назвою binutils-2.25(або будь-якої останньої версії) у домашньому каталозі Cygwin.
  4. Завантажте джерело OpenJDK, перейшовши до сховища оновлень JDK 8 , вибравши тег, що відповідає встановленій версії JRE, та клацнувши bz2. Витягніть каталог hsdis (знайдений у src\share\tools) до домашнього каталогу Cygwin.
  5. У терміналі Cygwin введіть cd ~/hsdis.
  6. Для побудови 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 не розпізнає його як такий.

  7. Збірка не вдасться з повідомленнями ./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


1
Попередньо побудовані двійкові файли для інших платформ - kenai.com/projects/base-hsdis/downloads
— Ashwin

@AshwinJayaprakash Де я повинен розмістити ці файли в Mac OS?
— Корай Тугай

@KorayTugay помістив їх/usr/lib/
— Жан-Франсуа Савард

Я оновив відповідь, скопіювавши з останньої версії сторінки, на яку пов’язано посилання, але це підкреслює причину, по якій ми зазвичай посилаємось на зовнішні ресурси, а не копіюємо їх дослівно.
— Олександр Дубінський

@AleksandrDubinsky Дякую за оновлення. Я спеціально скопіював його: якщо цей сайт буде збитий, моя відповідь все одно буде автономною ...
— assylias

29

Вам потрібен плагін hsdis для використання PrintAssembly. Зручним вибором є плагін hsdis на основі бібліотеки FCML.

Його можна скомпілювати для UNIX-подібних систем, а в Windows ви можете використовувати попередньо вбудовані бібліотеки, доступні в розділі завантаження FCML на 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

Додаткові параметри конфігурації:

код Друк машинного коду перед мнемонікою.
intel Використовуйте синтаксис Intel.
газ Використовуйте синтаксис асемблера AT&T (сумісний з асемблером GNU).
dec Друкує IMM та переміщення як десяткові значення.
mpad = XX Заповнення мнемонічної частини інструкції.
cpad = XX Заповнення машинного коду.
seg Показує регістри сегментів за замовчуванням.
нулі Показати провідні нулі для літералів HEX.

Синтаксис Intel є типовим для Windows, тоді як AT&T є типовим для GNU / Linux.

Докладніше див. У довідковому посібнику бібліотеки FCML


Дякуємо за фіксацію ліб. Це чудово працює і на Linux. Я видаляю свої старі коментарі, щоб тримати безлад.
— Олександр Дубінський

У Linux після того, як я встановив libhsdis.so і зробив м'яке посилання на hsdis-amd64.so, я запустив команду java, вона prmpts не може знайти hsdis-amd64.so. Я перезавантажуюсь, а потім перезапускаю java, це нормально. Як уникнути перезавантаження, щоб м’яке посилання працювало миттєво? вийти?
— gfan

2
Лише невелике доповнення: У деяких дистрибутивах Linux ви можете просто встановити пакет, наприклад в Ubuntu: apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ). Будувати це самостійно може не знадобитися.
— Девід Георг


5

Я вважаю, що WinDbg був би корисним, якщо ви запускаєте його на машині Windows. Я щойно запустив одну баночку.

  • Потім я приєднався до процесу Java через Windbg
  • Розглянуті теми за командою ~ ; Було 11 потоків, 0 - основний робочий потік
  • Переключено на 0-потік - ~ 0 с
  • Переглянувши некерований стек викликів по 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-коду на 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-код із дампів пам'яті за допомогою провідника процесів та WinDbg.


4

Інший спосіб побачити машинний код та деякі дані про ефективність - це використовувати CodeAnalyst або OProfile AMD, які мають плагін Java для візуалізації виконання Java-коду як машинного коду.


0

Надрукуйте збірку своїх гарячих точок за допомогою профілів для парфуму JMH ( LinuxPerfAsmProfilerабо WinPerfAsmProfiler). JMH вимагає hsdisбібліотеки, оскільки вона покладається на PrintAssembly.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.