如何在JVM中查看JIT编译的代码?
有没有什么方法可以查看JVM在JVM中生成的本地代码?
假设您正在使用Sun Hotspot JVM。 添加标志
-XX:+ PrintOptoAssembly
无论你在运行什么。 这只会打印已被JIT的代码的程序集(即,你不会看到程序集非JIT的东西),但我认为这就是你想要的。 如果你想看看什么都可以,如果它被JIT'd你可以通过调整JIT的门槛:
-XX:CompileThreshold =#
一般用法
正如其他答案所解释的,您可以使用以下JVM选项运行:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
过滤一个特定的方法
您还可以使用以下语法筛选特定的方法:
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod
笔记:
- 您可能需要根据操作系统等将第二个参数放在引号内
- 如果方法被内联,你可能会错过一些优化
如何:在Windows上安装所需的库
如果您正在运行Windows, 本页面提供了有关如何构build和安装hsdis-amd64.dll
和hsdis-i386.dll
所需的说明。 我们在下面复制并扩展该页面的内容*以供参考:
在哪里获得预编译的二进制文件
您可以从fcml项目下载Windows预编译的二进制文件
- hsdis-amd64.dll
- hsdis-i386.dll
如何在Windows上构buildhsdis-amd64.dll
和hsdis-i386.dll
本指南的版本是在Windows 8.1 64位上使用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
类中)
-
-
运行Cygwinterminal。 这可以使用安装程序创build的桌面或开始菜单图标完成,并将创build您的Cygwin主目录(默认为
C:\cygwin\home\<username>\
或C:\cygwin64\home\<username>\
) 。 - 下载最新的GNU binutils源码包并将其内容提取到您的Cygwin主目录。 在撰写本文时,最新的软件包是
binutils-2.25.tar.bz2
。 这应该导致一个名为binutils-2.25
(或者最新版本)的目录在你的Cygwin主目录下。 - 通过转至JDK 8更新存储库 ,select与已安装的JRE版本对应的标签,然后单击bz2,下载OpenJDK源代码。 提取hsdis目录(在
src\share\tools
)到你的Cygwin主目录。 - 在Cygwinterminal中,input
cd ~/hsdis
。 -
要构build
hsdis-amd64.dll
,请inputmake OS=Linux MINGW=x86_64-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25
要构build
hsdis-i386.dll
,请inputmake OS=Linux MINGW=i686-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25
在任何一种情况下,用您下载的binutils版本replace
2.25
。OS=Linux
是必要的,因为尽pipeCygwin是类似Linux的环境,但hsdis makefile却无法识别它。 -
./chew: No such file or directory
将失败并显示消息./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
。 重新运行先前的命令。
现在可以通过将hsdis\build\Linux-amd64
或hsdis\build\Linux-i586
复制到您的JRE的bin\server
或bin\client
目录来安装DLL。 您可以通过searchjava.dll
在您的系统上find所有这些目录。
奖励提示:如果您更喜欢Intel -XX:PrintAssemblyOptions=intel
语法到AT&T,请指定-XX:PrintAssemblyOptions=intel
以及您使用的任何其他PrintAssembly选项。
*页面许可是Creative Commons
你需要一个hsdis插件来使用PrintAssembly
。 一个方便的select是基于FCML库的hsdis插件。
它可以编译为类UNIX系统,在Windows上,您可以使用Sourceforge上FCML 下载部分提供的预build库:
在Windows中安装:
- 解压缩dll(可以在hsdis-1.1.2-win32-i386.zip和hsdis-1.1.2-win32-amd64.zip中find)。
- 将dll复制到存在
java.dll
地方(使用Windowssearch)。 在我的系统上,我发现它在两个位置:-
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
其他configuration参数:
代码在助记符之前打印机器代码。
英特尔使用英特尔语法。
gas使用AT&T汇编程序语法(兼容GNU汇编程序)。
dec以十进制值forms显示IMM和位移。
mpad = XX填充指令的助记符部分。
cpad = XX填充机器代码。
seg显示默认段寄存器。
零在hex文字的情况下显示前导零。
英特尔语法是Windows的默认语法,而AT&T是GNU / Linux的默认语法。
有关更多详细信息,请参阅“ FCML库参考手册”
对于HotSpot(即Sun)JVM,即使在产品模式下:
http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly
一些组件需要:它需要一个插件。
我相信WinDbg会有帮助,如果你在Windows机器上运行它。 我刚刚运行一个jar子。
- 然后我通过Windbg连接到java进程
- 通过〜命令检查线程; 有11个线程,0个线程是主工作线程
- 切换到0线程 – 〜0s
-
通过kb的 unmanmaged 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上:
点击查看 – >反汇编。
点击编辑 – >转到地址。
把00402f68放在那里
得到了00402f68 55推ebp
00402f69 8bec mov ebp,esp
00402f6b 81ec80020000 sub esp,280h
00402f71 53推ebx
00402f72 56推esi
00402f73 57推edi
…等等
有关其他信息,请参阅示例如何使用进程pipe理器和WinDbg从内存转储中追溯JIT编写的代码。
查看机器码和一些性能数据的另一种方法是使用AMD的CodeAnalyst或OProfile,它们有一个Java插件来将执行的Java代码可视化为机器代码。
使用JMH的perfasm分析器( LinuxPerfAsmProfiler
或WinPerfAsmProfiler
)打印热点的程序集。 JMH需要使用hsdis
库,因为它依赖于PrintAssembly
。