有没有办法在JVM中看到JIT生成的本机代码?
答案 0 :(得分:68)
如其他答案所述,您可以使用以下JVM选项运行:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
您还可以使用以下语法过滤特定方法:
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod
注意:
如果您运行的是Windows,则this page会提供有关如何构建和安装hsdis-amd64.dll
和hsdis-i386.dll
以使其正常运行所需的说明。我们在下面复制并扩展该页面的内容*以供参考:
从哪里获得预建二进制文件
您可以从fcml项目
下载适用于Windows的预建二进制文件 如何在Windows上构建hsdis-amd64.dll
和hsdis-i386.dll
此版本的指南是使用64位Cygwin在Windows 8.1 64位上编写的,并生成hsdis-amd64.dll
Install 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>\
)。
binutils-2.25.tar.bz2
。这应该会导致Cygwin主目录中名为binutils-2.25
的目录(或任何最新版本)。src\share\tools
中找到)解压缩到您的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
。在Wordpad或Notepad ++等文本编辑器中编辑<Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile
,将SUBDIRS = doc po
(第342行,如果使用binutils 2.25)更改为SUBDIRS = po
。重新运行上一个命令。现在可以通过将DLL从hsdis\build\Linux-amd64
或hsdis\build\Linux-i586
复制到JRE的bin\server
或bin\client
目录来安装DLL。您可以通过搜索java.dll
找到系统中的所有此类目录。
额外提示:如果您更喜欢英特尔ASM语法到AT&amp; T,请指定-XX:PrintAssemblyOptions=intel
以及您使用的任何其他PrintAssembly选项。
*页面许可证是Creative Commons
答案 1 :(得分:41)
假设您正在使用Sun Hotspot JVM(即Oracle提供的java.com上的JVM),您可以添加标记
-XX:+ PrintOptoAssembly
运行代码时。这将打印出JIT编译器生成的优化代码,并将其余部分排除在外。
如果要查看整个字节码,包括未经优化的部分,请添加
-XX:CompileThreshold =#
当您运行代码时。
您可以阅读有关此命令和JIT功能的更多信息here。
答案 2 :(得分:25)
您需要一个hsdis插件才能使用PrintAssembly
。一个方便的选择是基于FCML库的hsdis插件。
可以为类UNIX系统编译,在Windows上,您可以使用Sourceforge上的FCML download部分中提供的预构建库:
java.dll
(使用Windows搜索)。在我的系统上,我在两个位置找到它:
C:\Program Files\Java\jre1.8.0_45\bin\server
C:\Program Files\Java\jdk1.8.0_45\jre\bin\server
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
/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语法
gas 使用AT&amp; T汇编语法(兼容GNU汇编程序)
dec 以十进制值打印IMM和位移
mpad = XX 填充指令的助记符部分。
cpad = XX 填写机器代码。
seg 显示默认的段寄存器
zeros 在HEX文字的情况下显示前导零。
对于Windows,英特尔语法是默认语法,而AT&amp; T语言是GNU / Linux的默认语法。
有关详细信息,请参阅FCML Library Reference Manual
答案 3 :(得分:8)
对于HotSpot(是Sun)JVM,即使在产品模式下:
http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly
需要一些程序集:它需要一个插件。
答案 4 :(得分:5)
我相信如果你在Windows机器上运行WinDbg会有所帮助。 我刚跑了一个罐子。
通过 kb 查看未受损的callstack,有:
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
突出显示的行是在JVM上直接运行JIT编码。
然后我们可以找到方法地址:
java + 0x2f68是00402f68
在WinDBG上:
点击查看 - &gt;
拆卸。
点击编辑 - &gt;去
地址。
将 00402f68 放在那里
并得到了
00402f68 55推ebp
00402f69 8bec mov ebp,esp
00402f6b 81ec80020000 sub esp,280h
00402f71 53推ebx
00402f72 56推esi
00402f73 57推edi
...... 等等
有关其他信息,请参阅Example如何使用process explorer和WinDbg从内存转储中追溯JIT编码。
答案 5 :(得分:4)
查看机器代码和一些性能数据的另一种方法是使用AMD的CodeAnalyst或OProfile,它有一个Java插件,可以将执行的Java代码可视化为机器代码。
答案 6 :(得分:0)
使用JMH的perfasm探查器(LinuxPerfAsmProfiler
或WinPerfAsmProfiler
)打印热点的程序集。 JMH确实需要hsdis
库,因为它依赖于PrintAssembly
。