java 如何在 JVM 中查看 JIT 编译的代码?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1503479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 16:48:30  来源:igfitidea点击:

How to see JIT-compiled code in JVM?

javaassemblyjvmjit

提问by alsor.net

Is there some way to see the native code produces by the JIT in a JVM?

有没有办法在 JVM 中查看 JIT 生成的本机代码?

采纳答案by Falaina

Assuming you're using the Sun Hotspot JVM (i.e. the one provided on java.comby Oracle), you can add the flag

假设您使用的是 Sun Hotspot JVM(即Oracle在java.com 上提供的那个),您可以添加标志

-XX:+PrintOptoAssembly

-XX:+PrintOptoAssembly

when running your code. This will print out the optimized code generated by the JIT compiler and leaves out the rest.

运行代码时。这将打印出由 JIT 编译器生成的优化代码并忽略其余部分。

If you want see the entire bytecode, including the unoptimized parts, add

如果您想查看整个字节码,包括未优化的部分,请添加

-XX:CompileThreshold=#

-XX:CompileThreshold=#

when you're running your code.

当你运行你的代码时。

You can read more about this command and the functionality of JIT in general here.

您可以在此处阅读有关此命令和 JIT 一般功能的更多信息。

回答by assylias

General usage

一般用途

As explained by other answers, you can run with the following JVM options:

正如其他答案所述,您可以使用以下 JVM 选项运行:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Filter on a specific method

过滤特定方法

You can also filter on a specific method with the following syntax:

您还可以使用以下语法过滤特定方法:

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

Notes:

笔记:

  • you might need to put the second argument within quotes depending on OS etc.
  • if the method gets inlined, you could miss some optimisations
  • 根据操作系统等,您可能需要将第二个参数放在引号内。
  • 如果方法被内联,你可能会错过一些优化

How to: Install the required libraries on Windows

如何:在 Windows 上安装所需的库

If you are running Windows, this pagehas instructions on how to build and install hsdis-amd64.dlland hsdis-i386.dllwhich are required to make it work. We copy below and extend the content of that page* for reference:

如果您运行的是 Windows,此页面包含有关如何构建和安装hsdis-amd64.dll以及hsdis-i386.dll使其工作所需的说明。我们复制并扩展该页面*的内容以供参考:



Where to get prebuilt binaries

从哪里获得预构建的二进制文件

You can download prebuilt binaries for Windows from the fcmlproject

您可以从fcml项目下载适用于 Windows 的预构建二进制文件

How to build hsdis-amd64.dlland hsdis-i386.dllon Windows

如何构建hsdis-amd64.dllhsdis-i386.dll在 Windows 上

This version of the guide was prepared on Windows 8.1 64bit using 64-bit Cygwin and producing hsdis-amd64.dll

此版本的指南是在 Windows 8.1 64bit 上使用 64 位 Cygwin 编写的,并生成 hsdis-amd64.dll

  1. Install Cygwin. At the Select Packagesscreen, add the following packages (by expanding the Develcategory, then clicking once on the Skiplabel next to each package name):

    • make
    • mingw64-x86_64-gcc-core(only needed for hsdis-amd64.dll)
    • mingw64-i686-gcc-core(only needed for hsdis-i386.dll)
    • diffutils(in Utilscategory)
  2. Run the Cygwin Terminal. This can be done using the Desktop or Start Menu icon created by the installer, and will create your Cygwin home directory (C:\cygwin\home\<username>\or C:\cygwin64\home\<username>\by default).

  3. Download the latest GNU binutils source packageand extract its contents to your Cygwin home directory. At the time of writing, the latest package is binutils-2.25.tar.bz2. This should result in a directory named binutils-2.25(or whatever the latest version is) in your Cygwin home directory.
  4. Download the OpenJDK source by going to the JDK 8 Updates repository, selecting the tag corresponding to your installed JRE version, and clicking bz2. Extract the hsdis directory (found in src\share\tools) to your Cygwin home directory.
  5. In the Cygwin Terminal, enter cd ~/hsdis.
  6. To build hsdis-amd64.dll, enter

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

    To build hsdis-i386.dll, enter

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

    In either case, replace 2.25with the binutils version you downloaded. OS=Linuxis necessary because, although Cygwin is a Linux-like environment, the hsdis makefile fails to recognize it as such.

  7. The build will fail with messages ./chew: No such file or directoryand gcc: command not found. Edit <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefilein a text editor like Wordpad or Notepad++ to change SUBDIRS = doc po(line 342, if using binutils 2.25) to SUBDIRS = po. Re-run the previous command.
  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. 通过转至 JDK 8 更新存储库,选择与您安装的 JRE 版本对应的标签,然后单击 bz2下载 OpenJDK 源代码。将 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 生成文件无法识别它。

  7. 构建将失败并显示消息./chew: No such file or directorygcc: command not found. 编辑<Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile在像写字板或记事本++来改变文本编辑器SUBDIRS = doc po(342线中,如果使用的binutils 2.25)到SUBDIRS = po。重新运行之前的命令。

The DLL can now be installed by copying it from hsdis\build\Linux-amd64or hsdis\build\Linux-i586to your JRE's bin\serveror bin\clientdirectory. You can find all such directories on your system by searching for java.dll.

现在可以通过从hsdis\build\Linux-amd64或复制hsdis\build\Linux-i586到 JREbin\serverbin\client目录来安装 DLL 。您可以通过搜索来找到系统上的所有此类目录java.dll

Bonus tip: if you prefer Intel ASM syntax to AT&T, specify -XX:PrintAssemblyOptions=intelalongside any other PrintAssembly options you use.

额外提示:如果您更喜欢英特尔 ASM 语法而不是 AT&T,请-XX:PrintAssemblyOptions=intel与您使用的任何其他 PrintAssembly 选项一起指定。

*page license is Creative Commons

*页面许可是知识共享

回答by swojtasiak

You need an hsdis plugin to use PrintAssembly. A convenient choice is the hsdis plugin based on FCML library.

您需要一个 hsdis 插件才能使用PrintAssembly. 一个方便的选择是基于 FCML 库的 hsdis 插件。

It can be compiled for UNIX-like systems and on Windows you can use pre-built libraries available in the FCML downloadsection on Sourceforge:

它可以针对类 UNIX 系统进行编译,在 Windows 上,您可以使用Sourceforge 上的 FCML下载部分中提供的预构建库:

To install in Windows:

在 Windows 中安装:

  • Extract the dll (it can be found in hsdis-1.1.2-win32-i386.zip and hsdis-1.1.2-win32-amd64.zip).
  • Copy the dll to wherever exists java.dll(use Windows search). On my system, I found it at two locations:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server
  • 提取 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

To install in Linux:

在 Linux 中安装:

  • Download source code, extract it
  • 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
  • On my system, the JDK is in /usr/lib/jvm/java-8-oracle
  • 下载源代码,解压
  • 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

How to run it:

如何运行它:

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

Additional configuration parameters:

附加配置参数:

codePrint machine code before the mnemonic.
intelUse the Intel syntax.
gasUse the AT&T assembler syntax (GNU assembler compatible).
decPrints IMM and displacement as decimal values.
mpad=XXPadding for the mnemonic part of the instruction.
cpad=XXPadding for the machine code.
segShows the default segment registers.
zerosShow leading zeros in case of HEX literals.

code在助记符之前打印机器代码。
intel使用 Intel 语法。
gas使用 AT&T 汇编器语法(GNU 汇编器兼容)。
dec 将IMM 和位移打印为十进制值。
mpad=XX填充指令的助记符部分。
cpad=XX 机器码的填充。
seg显示默认段寄存器。
zeros在十六进制文字的情况下显示前导零。

The Intel syntax is a default one in case of Windows, whereas the AT&T one is a default for the GNU/Linux.

Intel 语法是 Windows 的默认语法,而 AT&T 语法是 GNU/Linux 的默认语法。

For more details see the FCML Library Reference Manual

有关更多详细信息,请参阅FCML 库参考手册

回答by John Rose

For the HotSpot (was Sun) JVM, even in product modes:

对于 HotSpot(以前是 Sun)JVM,即使在产品模式中:

http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly

http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly

Some assembly required: it needs a plugin.

需要一些程序集:它需要一个插件。

回答by Andriy Tkach

I believe WinDbg would be helpful if you are running it on windows machine. I have just run one jar.

如果您在 Windows 机器上运行它,我相信 WinDbg 会有所帮助。我刚刚运行了一个罐子。

  • Then I attached to the java process through Windbg
  • Examined threads by ~command; There were 11 threads, 0 thread was main worker thread
  • Switched to 0-thread - ~0s
  • Looked through unmanmaged callstack by kbthere was:

    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

  • 然后我通过Windbg附加到java进程
  • 通过~命令检查线程;有 11 个线程,0 个线程是主工作线程
  • 切换到 0 线程 - ~0s
  • 通过kb查看非托管调用堆栈有:

    0008fba8 7c90e9c0 NTDLL!KiFastSystemCallRet
    0008fbac 7c8025cb NTDLL!ZwWaitForSingleObject + 0xC的
    0008fc10 7c802532 KERNEL32!WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 KERNEL32!WaitForSingleObject的+ 0×12
    0008fc40 00402f68的java + 0x3a13
    0008fee4 004087b8的java + 0x2f68
    0008ffc0 7c816fd7的java + 0x87b8

    0008fff0 00000000 KERNEL32!BaseProcessStart + 0×23

Highlighted lines is direct running JIT-ed code on JVM.

突出显示的行是在 JVM 上直接运行 JIT 代码。

  • Then we can look for method address:
    java+0x2f68 is 00402f68

  • On WinDBG:
    Click View --> Disassembly.
    Click Edit --> Go to Address.
    Put 00402f68there
    and got

    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
    ...and so on

  • 然后我们可以查找方法地址:
    java+0x2f68就是00402f68

  • 在 WinDBG 上:
    单击查看 --> 反汇编。
    单击编辑 --> 转到地址。
    00402f68放在那里
    并得到

    00402f68 55推EBP
    00402f69 8bec MOV EBP,ESP
    00402f6b 81ec80020000子ESP,280H
    00402f71 53推EBX
    00402f72 56推ESI
    00402f73 57推EDI
    ......等等

For additional info here is the Examplehow to trace back JIT-ed code from memory dumps using process explorer and WinDbg.

此处的其他信息是如何使用进程资源管理器和 WinDbg 从内存转储中追溯 JIT 代码的示例

回答by Ian

Another way to see machine code and some performance data is to use AMD's CodeAnalyst or OProfile, which have a Java plugin to visualize executing Java code as machine code.

查看机器代码和一些性能数据的另一种方法是使用 AMD 的 CodeAnalyst 或 OProfile,它们有一个 Java 插件,可以将执行的 Java 代码可视化为机器代码。

回答by Aleksandr Dubinsky

Print the assembly of your hotspots with JMH's perfasm profilers (LinuxPerfAsmProfileror WinPerfAsmProfiler). JMH does require the hsdislibrary since it relies on PrintAssembly.

使用 JMH 的 perfasm 分析器(LinuxPerfAsmProfilerWinPerfAsmProfiler)打印热点的集合。JMH 确实需要该hsdis库,因为它依赖于PrintAssembly.