JITWatch实战指南:深入Java即时编译优化的黑科技工具

写在文章开头

在当今软件开发领域,性能优化已成为提升应用质量和用户体验的关键环节。JITWatch 作为一款专业的Java性能分析工具,能够帮助开发者深入了解Java应用的即时编译(Just-In-Time Compilation, JIT)过程,从而有效识别和解决性能瓶颈。本文将为读者提供一个全面而实用的JITWatch使用指南,通过实际案例和详细步骤介绍如何利用这一工具进行高效的性能分析与优化。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili ,也欢迎您了解我的开源项目 mini-redis:github.com/shark-ctrl/...

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

快速上手JITWatch

下载与配置

我们首先需要到GitHub上下载对应的源码包,对应的地址为:github.com/AdoptOpenJD...

需要注意的是,AdoptOpenJDK项目已经迁移,现在由GraalVM社区维护,新地址为: github.com/graalvm/jit...

需要注意的是1.4.2以上的版本可能存在乱码的情况,所以我们建议clone的时候尽量选择1.4.2及以下的版本。这里笔者也给出自己的克隆指令:

scss 复制代码
git clone --branch 1.4.1  git@github.com:AdoptOpenJDK/jitwatch.git

完成之后,我们还需要进行hsdis的下载安装,对应的dll文件笔者也已经打包好了,需要的读者可以扫描笔者下方的公众号回复hsdis即可获取:

完成下载之后,我们只需要将其放到系统jdk目录的\jre\bin\server目录下:

基础使用演示

通过上述步骤我们完成了最基础的配置和安装,接下来我们就来一个比较基础的演示,我们到jitwatch目录下执行如下命令启动程序:

bash 复制代码
mvn clean compile test exec:java

完成启动后我们点击sandbox:

点击后我们可以看到一个基础的带有基本代码示例的界面,我们点击配置:

然后勾选显示反编译和显示Intel的汇编码,完成后点击save

完成这些配置后,回到默认界面我们直接点击run,即可看到对应的字节码和汇编码指令:

基于jitwatch了解volatile可见性的汇编语义

在深入使用JITWatch之前,我们需要了解一下JIT编译的优化级别。JVM的JIT编译器主要有两种:

  1. C1编译器:也称为Client编译器,主要进行局部优化,编译速度快但优化程度较低
  2. C2编译器:也称为Server编译器,会进行全局优化,编译速度慢但优化程度高

JVM会根据方法的执行频率来决定使用哪种编译器,通常会先使用C1编译器进行即时编译,当方法被频繁调用时再使用C2编译器进行深度优化。

JITWatch除了可以查看汇编码外,还提供了其他重要功能:

  1. 热点代码分析:可以查看哪些方法被JIT编译器编译
  2. 内联分析:可以查看方法内联的情况
  3. 逃逸分析:可以查看对象逃逸分析的结果
  4. 锁消除:可以查看锁消除优化的情况

此外,了解JIT编译的触发条件对于有效使用JITWatch也很重要:

  • 编译阈值:默认情况下,方法被调用10000次或循环执行10000次后会触发JIT编译(在JDK 8及以后版本中,默认值可能因平台和JVM实现而异)
  • 分层编译:HotSpot JVM采用分层编译策略,结合了解释执行、C1编译和C2编译的优势

在使用JITWatch进行性能分析时,还需要注意以下几点:

  • JVM参数优化 :除了基本的参数外,还可以使用-XX:+PrintCompilation来查看编译日志,使用-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining来查看内联信息
  • 日志文件大小:生产环境中的日志文件可能非常大,建议在测试环境中进行分析,或者使用过滤器只记录关注的类或方法
  • 版本兼容性:不同版本的JDK生成的日志格式可能略有差异,建议使用与目标环境相同版本的JDK进行分析

接下来我们通过一个实际案例来演示JITWatch的进阶使用。笔者的Maven项目有下面这样一段代码,我们希望查看volatile关键字底层汇编码的实现:

arduino 复制代码
private volatile static int num = 0;

    public static void main(String[] args) throws InterruptedException {
        // 注意:volatile不能保证num++操作的原子性
        // 这里仅用于演示volatile的内存可见性语义
        num++;
    }

接下来我们打开JITWatch,然后点击配置按钮,选择上述代码对应的源代码路径和字节码路径:

然后找到我们的项目,在运行时添加如下JVM参数并运行项目,以便生成JIT编译日志:

ruby 复制代码
 -Xcomp  -XX:+UnlockDiagnosticVMOptions  -XX:+PrintAssembly  -XX:+LogCompilation  -XX:LogFile=jit.log 

完成运行后,我们会在项目中看到一个名为jit.log的文件:

最后我们回到jitwatch打开这个日志:

最后点击Start按钮,此时JITWatch就会处理字节码和汇编码指令的解析和映射:

完成后,找到我们代码的包路径,点击main方法:

最终我们就可以看到对应volatile自增代码段对应的字节码和汇编码,如下便是num++的字节码的复合操作:

yaml 复制代码
0: getstatic       #2   // Field num:I
3: iconst_1        
4: iadd            
5: putstatic       #2   // Field num:I
8: return          

对应的汇编如下:

  1. 将num放到寄存器
  2. 执行inc自增
  3. 通过lock前缀将cpu缓存中累加的num强制写回内存中
perl 复制代码
0x00000000039605b6: mov 0x68(%rsi),%edi  ;*getstatic num
                                         ; - com.sharkchili.Main::main@0 (line 11)
0x00000000039605b9: inc %edi
0x00000000039605bb: mov %edi,0x68(%rsi)
0x00000000039605be: lock addl $0x0,(%rsp)  ;*putstatic num
                                           ; - com.sharkchili.Main::main@5 (line 11)

对应的观测截图如下,可以看到我们可以基于代码段做到字节码和汇编码的对照:

macOS版本的JITWatch配置与使用

反编译工具下载与配置

和Windows版本类似,macOS下载和克隆JITWatch的指令和版本都是一样的:

scss 复制代码
git clone --branch 1.4.1  git@github.com:AdoptOpenJDK/jitwatch.git

这里主要补充说明下macOS的JDK如何配置反编译插件hsdis,首先自然是下载反编译插件,对应下载地址为:

chriswhocodes.com/hsdis/

以笔者的macOS为例,对应下载M芯片版本:

完成下载后将其移动到jdk8的server目录下,这里笔者贴出自己的移动指令,读者可参考相对路径定位到自己的插件位置:

bash 复制代码
mv hsdis-aarch64.dylib /Users/sharkchili/dev-tool/jdk8/Contents/Home/jre/lib/server

注意:上述安装方式适用于免安装版本的JDK 8。对于通过安装包安装的JDK,路径通常为:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/server/

启动和使用

JITWatch在macOS上的启动和使用方式与Windows基本相同,具体步骤如下:

  1. 配置源代码和输出字节码路径
  2. 基于JVM配置参数启动代码
  3. 获取jit.log并通过JITWatch的Open按钮打开
  4. 点击Start开始分析

最终我们就可以看到arm架构下的jit优化后的汇编码了:

小结

JITWatch作为一款强大的Java性能分析工具,能够帮助开发者深入了解JVM的即时编译过程,从字节码到汇编码的完整映射。通过本文的介绍,我们掌握了在不同操作系统上配置和使用JITWatch的方法,并通过实际案例演示了如何分析volatile关键字的底层实现。

正确使用JITWatch不仅需要掌握工具本身的使用方法,更需要理解JVM的编译原理和优化机制。在实际性能调优工作中,我们应当结合多种工具和方法,从多个维度分析和解决性能问题。

随着JVM技术的不断发展,JIT编译器的优化策略也在持续演进。掌握这些底层原理,有助于我们编写出更高效的Java代码,在实际开发中做出更合理的性能优化决策。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili ,也欢迎您了解我的开源项目 mini-redis:github.com/shark-ctrl/...

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入.

参考

jitwatch:github.com/AdoptOpenJD...

JITWatch很折腾?有这篇文章在可以放心 :blog.csdn.net/m0_73311735...

如何优雅的查看 Java 代码的汇编码 :blog.csdn.net/haihui_yang...

使用hsdis与jitwatch查看JIT后的汇编码:www.jianshu.com/p/78f71c033...

适用于Mac M1的hsdis-aarch64.dylib:blog.csdn.net/azs1478963/...

Mac 安装 hsdis 查看 java 汇编指令:blog.csdn.net/qq_28163609...

JIT中的PrintAssembly续集:blog.csdn.net/m0_49133785...

查看java汇编源码报错:Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled:blog.csdn.net/lhm96451747...

相关推荐
绝无仅有4 小时前
从拉取代码到前端运行访问:Vue 前端项目的常规启动流程
后端·面试·github
小蒜学长4 小时前
spring boot驴友结伴游网站的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
CodeLongBear4 小时前
深入理解 JVM 字节码文件:从组成结构到 Arthas 工具实践
java·jvm·后端
IT_陈寒4 小时前
SpringBoot 3.x实战:5种高并发场景下的性能优化秘籍,让你的应用快如闪电!
前端·人工智能·后端
Victor3565 小时前
Redis(47)如何配置Redis哨兵?
后端
Victor3565 小时前
Redis(46) 如何搭建Redis哨兵?
后端
尘鹄10 小时前
go 初始化组件最佳实践
后端·设计模式·golang
墩墩分墩10 小时前
【Go语言入门教程】 Go语言的起源与技术特点:从诞生到现代编程利器(一)
开发语言·后端·golang·go
程序员爱钓鱼12 小时前
Go语言实战案例- 开发一个ToDo命令行工具
后端·google·go