有什么方法可以查看 JIT 在 JVM 中生成的本机代码吗?
7 回答
一般用法
正如其他答案所解释的,您可以使用以下 JVM 选项运行:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
过滤特定方法
您还可以使用以下语法过滤特定方法:
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod
笔记:
- 您可能需要根据操作系统等将第二个参数放在引号内。
- 如果方法被内联,你可能会错过一些优化
如何:在 Windows 上安装所需的库
如果您运行的是 Windows,此页面包含有关如何构建和安装hsdis-amd64.dll
以及hsdis-i386.dll
使其工作所需的说明。我们在下面复制并扩展该页面*的内容以供参考:
从哪里获得预构建的二进制文件
您可以从fcml项目下载适用于 Windows 的预构建二进制文件
如何在 Windowshsdis-amd64.dll
上构建hsdis-i386.dll
此版本的指南是在 64 位 Windows 8.1 上使用 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
类别中)
运行 Cygwin 终端。这可以使用安装程序创建的桌面或开始菜单图标来完成,并将创建您的 Cygwin 主目录(
C:\cygwin\home\<username>\
或C:\cygwin64\home\<username>\
默认情况下)。- 下载最新的 GNU binutils 源包并将其内容解压缩到 Cygwin 主目录。在撰写本文时,最新的软件包是
binutils-2.25.tar.bz2
. 这应该会在 Cygwin 主目录中生成一个名为binutils-2.25
(或任何最新版本)的目录。 - 通过转到 JDK 8 Updates 存储库下载 OpenJDK 源代码,选择与您安装的 JRE 版本相对应的标签,然后单击 bz2。将 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++ 等文本编辑器中编辑<Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile
以将SUBDIRS = doc po
(第 342 行,如果使用 binutils 2.25)更改为SUBDIRS = po
. 重新运行之前的命令。
现在可以通过从JREhsdis\build\Linux-amd64
或目录复制它来安装 DLL。您可以通过搜索找到系统上的所有此类目录。hsdis\build\Linux-i586
bin\server
bin\client
java.dll
额外提示:如果您更喜欢 Intel ASM 语法而不是 AT&T,-XX:PrintAssemblyOptions=intel
请在您使用的任何其他 PrintAssembly 选项旁边指定。
*页面许可是知识共享
您需要一个 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 <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
附加配置参数:
code在助记符前打印机器码。
intel使用 Intel 语法。
gas使用 AT&T 汇编语法(GNU 汇编兼容)。
dec将 IMM 和位移打印为十进制值。
mpad=XX指令助记符部分的填充。
cpad=XX 机器码的填充。
seg显示默认的段寄存器。
zeros在 HEX 文字的情况下显示前导零。
Intel 语法是 Windows 的默认语法,而 AT&T 语法是 GNU/Linux 的默认语法。
有关更多详细信息,请参阅FCML 库参考手册
对于 HotSpot(以前是 Sun)JVM,即使在产品模式下:
http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly
需要一些组件:它需要一个插件。
如果您在 Windows 机器上运行它,我相信 WinDbg 会很有帮助。我刚刚运行了一个罐子。
- 然后我通过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
突出显示的行是在 JVM 上直接运行 JIT-ed 代码。
然后我们可以查找方法地址:
java+0x2f68 is 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
...等等
有关其他信息,这里是如何使用进程资源管理器和 WinDbg 从内存转储中追溯 JIT 代码的示例。
另一种查看机器代码和一些性能数据的方法是使用 AMD 的 CodeAnalyst 或 OProfile,它们有一个 Java 插件,可以将执行的 Java 代码可视化为机器代码。
使用 JMH 的 perfasm 分析器 (LinuxPerfAsmProfiler
或WinPerfAsmProfiler
) 打印您的热点组件。JMH 确实需要该hsdis
库,因为它依赖于PrintAssembly
.