有没有办法在JVM中看到JIT生成的本机代码?
您确定要查看JIT编译(本机)代码,还是仅查看字节代码? 我问,因为在这里提出这个问题会导致一些疑问,如果你真的想看到本机代码......而且,对不起,我也不知道这样的工具。
我想看看exaclty JIT编译的本机代码。 当然,我不需要完成工作,而是进行实验和调查。
一般用法
如其他答案所述,您可以使用以下JVM选项运行:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
过滤特定方法
您还可以使用以下语法过滤特定方法:
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod
笔记:
您可能需要根据操作系统等将第二个参数放在引号内。
如果方法被内联,你可能会错过一些优化
如何:在Windows上安装所需的库
如果您运行的是Windows,则此页面提供了有关如何构建和安装hsdis-amd64.dll和hsdis-i386.dll以使其正常工作的说明。我们在下面复制并扩展该页面的内容*以供参考:
哪里可以获得预建的二进制文件
您可以从fcml项目下载Windows的预构建二进制文件
hsdis-amd64.dll
hsdis-i386.dll
如何在Windows上构建hsdis-amd64.dll和hsdis-i386.dll
此版本的指南是使用64位Cygwin在Windows 8.1 64位上编写的,并生成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\\或C:\cygwin64\home\\)。
下载最新的GNU binutils源包并将其内容解压缩到Cygwin主目录。在撰写本文时,最新的软件包binutils-2.25.tar.bz2。这应该会导致Cygwin主目录中名为binutils-2.25(或最新版本)的目录。
通过转到JDK 8 Updates存储库,选择与已安装的JRE版本对应的标记,然后单击bz2,下载OpenJDK源。将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。在Wordpad或Notepad ++等文本编辑器中编辑\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。您可以通过搜索java.dll在系统中找到所有此类目录。
额外提示:如果您更喜欢英特尔ASM语法到AT&T,请指定-XX:PrintAssemblyOptions=intel以及您使用的任何其他PrintAssembly选项。
*页面许可证是Creative Commons sub>
其他平台的预构建二进制文件 - kenai.com/projects/base-hsdis/downloads
@AshwinJayaprakash我应该把这些文件放在Mac OS中?
@KorayTugay将它们放在/usr/lib/中
我通过从最新版本的链接页面进行复制来更新答案,但这突出了我们通常链接到外部资源而不是逐字复制它们的原因。
@AleksandrDubinsky感谢您的更新。我故意复制它:如果该网站被删除我的答案仍然是自包含的......
仅供参考,请勿使用此其他指南jpbempel.blogspot.com/2013/07/how-to-build-hsdis-amd64dll.html尝试使用MSYS / MinGW构建hsdis。它不起作用。
假设您正在使用Sun Hotspot JVM(即Oracle提供的java.com上的JVM),您可以添加标志
-XX:+PrintOptoAssembly
在运行代码时。这将打印出JIT编译器生成的优化代码,并将其余部分排除在外。
如果要查看整个字节码,包括未经优化的部分,请添加
-XX:CompileThreshold=#
当你运行你的代码时。
您可以在此处阅读有关此命令和JIT功能的更多信息。
此选项仅出现在调试版本中或其他任何内容中吗?因为我的JVM("Java(TM)SE运行时环境(版本1.6.0_16-b01")无法识别它,即使Web上的源指示此功能在Sun Java 6和OpenJDK中可用。
是的,需要DEBUG二进制文件。 blogs.warwick.ac.uk/richardwarburton/entry/
不应该(现在)-XX:+ PrintAssembly,至少现在?在我的机器上测试,并匹配这里所说的内容:wikis.sun.com/display/HotSpotInternals/PrintAssembly在此选项和反汇编程序插件之前,您需要-XX:+ UnlockDiagnosticVMOptions。
@Blaisorblade我得到:错误指定的VM选项'PrintAssembly'错误:无法创建Java虚拟机。错误:发生了致命异常。程序将会退出。
@KorayTugay请参阅其他答案 - 更新后的链接是wikis.oracle.com/display/HotSpotInternals/PrintAssembly,如stackoverflow.com/a/15146962/53974或stackoverflow.com/a/4149878/53974中所述。如果以下说明不起作用,请在适当的地方询问详细信息(不确定是否应该针对您的案例提出另一个问题,参考此案例)。
您需要一个hsdis插件才能使用PrintAssembly。一个方便的选择是基于FCML库的hsdis插件。
它可以为类UNIX系统编译,在Windows上,您可以使用Sourceforge上的FCML下载部分中提供的预构建库:
要在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
./configure && make && sudo make install
<5233>
sudo ln -s /usr/local/lib/libhsdis.so /lib/amd64/hsdis-amd64.so
sudo ln -s /usr/local/lib/libhsdis.so /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语法。
gas使用AT&T汇编语法(兼容GNU汇编程序)
dec将IMM和位移打印为十进制值
mpad = XX填充指令的助记符部分。
cpad = XX填充机器代码。
seg显示默认的段寄存器
零在HEX文字的情况下显示前导零
对于Windows,Intel语法是默认语法,而AT&T语言是GNU / Linux的默认语法。
有关更多详细信息,请参阅"FCML库参考手册"
感谢您修复lib。它在Linux上也很棒。我正在删除我的旧评论以保持混乱。
在Linux上,在我安装libhsdis.so并建立到hsdis-amd64.so的软链接后,我运行java命令,它prmpts找不到hsdis-amd64.so。我重启然后重新运行java,没关系。如何避免重启以使软链接立即生效?登出?
只需添加一点:在某些Linux发行版中,您只需安装一个软件包,例如在Ubuntu中:apt-get install libhsdis0-fcml(askubuntu.com/a/991166/489909)。自己构建这个可能没有必要。
对于HotSpot(是Sun)JVM,即使在产品模式下:
http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly
需要一些程序集:它需要一个插件。
您的答案中的链接似乎已经消失。请您更新一下吗?提前致谢。
我相信如果你在Windows机器上运行WinDbg会有所帮助。
我刚跑了一个罐子。
然后我附加了java进程
通过Windbg
通过?命令检查线程;有11个线程,0个线程是主要工作线程
切换到0线程 - ~0s
通过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上:单击"查看" - >
拆卸。单击编辑 - >转到
地址。把00402f68放在那里
得到了
00402f68 55推ebp
00402f69 8bec mov ebp,尤其是
00402f6b 81ec80020000 sub esp,280h
00402f71 53推ebx
00402f72 56推esi
00402f73 57推edi
... 等等
有关其他信息,请参阅示例如何使用process explorer和WinDbg从内存转储中追溯JIT-ed代码。
查看机器代码和一些性能数据的另一种方法是使用AMD的CodeAnalyst或OProfile,它具有Java插件,可将执行的Java代码可视化为机器代码。
使用JMH的perfasm剖析器(LinuxPerfAsmProfiler或WinPerfAsmProfiler)打印热点的组件。 JMH确实需要hsdis库,因为它依赖于PrintAssembly。