Jtoss Jtoss
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档

Jason Huang

后端程序猿
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档
  • IDEA系列

    • IDEA 2021 开发环境
    • IDEA 快捷键
  • GIt系列

  • 其他

  • 工具
  • 其他
Jason
目录

hsdis 和 jitwatch

# hsdis 和 jitwatch

# 介绍

学习并发时,关于 volatile 关键字的实现原理: **编译器将在volatile字段的读写操作前后各插入一些内存屏障。**其使用到了汇编码中的 lock 指令前缀。于是就产生了一个想法: Java 代码生成的汇编码是什么样子的?如何将 Java 代码与汇编码相对应?

比如这个类:

public class TestVolatile {
    public volatile long sum = 0;

    public int add(int a, int b) {
        int temp = a + b;
        sum += temp;
        return temp;
    }

    public static void main(String[] args) {
        TestVolatile test = new TestVolatile();

        int sum = 0;

        for (int i = 0; i < 1000000; i++) {
            sum = test.add(sum, 1);
        }

        System.out.println("Sum:" + sum);
        System.out.println("Test.sum:" + test.sum);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

而通过 hsdis 和 jitwatch 工具可以得到编译后的汇编代码。

安装环境:

  • Mac OS 12.6
  • jdk1.8.0_131

# 安装 hsdis

hsdis(HotSpot disassembly) 是 Sun 官方推荐的 HotSpot VM JIT 编译代码的反汇编插件。由于使用 jdk8,需要引入 hsdis-amd64.dylib

  1. 下载 hsdis:百度网盘 (opens new window),密码:uigv
  2. 将 hsdis-adm64.dylib 文件放到 jre/lib 目录下即可,本机目录地址:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib

# 配置 IntelliJ IDEA 运行参数,获取汇编日志

下面会用到的 java 命令参数的解释:

-XX:+UnlockDiagnosticVMOptions:解锁用于 JVM 诊断的选项。

-XX:+PrintAssembly:配合反汇编插件(例如 hsdis-amd64.dylib)可以打印出字节码和本地方法的汇编码;必须和 -XX:+UnlockDiagnosticVMOptions 一起使用。

-Xcomp:在第一次调用时强制编译方法。默认情况下,无论是 -client 模式还是 -server 模式,都需要执行一定次数解释方法的调用才会触发方法的编译。(如果需要 JIT 日志,则不指定该参数)

-XX:CompileCommand=compileonly,*ClassName.methodName:只编译类名为 ClassName 中的 methodName 方法,支持使用 * 作为通配符。可以多次指定 -XX:CompileCommand 添加多条命令。(建议只指定需要的方法,否则将会产生大量的无关日志)

-XX:+LogCompilation:允许将编译活动记录到当前工作目录中名为 hotspot.log 的文件中。可以通过 -XX:LogFile 指定文件的路径和名字。

IDEA 运行参数可简单配置为:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly  
1

hsdis1

IDEA 执行结果:

hsdis2

也可以指定类中某个方法,结果保存到文件中,配置如下:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:CompileCommand=compileonly,*VisibilityTest.add  -XX:+LogCompilation -XX:LogFile=~/VisibilityTest.log
1

# 安装 jitwatch

使用 IDEA 或 文本编辑器查看汇编 log 文件不太方便,可以是 jitwatch 工具。jitwatch 是 GitHub 上的一个开源项目,一个用于分析汇编日志的图形界面工具。

  1. 下载 jitwatch:AdoptOpenJDK/jitwatch (opens new window)

  2. 启动:mvn clean compile test exec:java

  3. 配置:

    jitwatch1

# 使用 jitwatch sandbox 分析 JIT 汇编代码

Sandbox 的作用呢就是直接运行代码里面的 main 函数,然后根据代码的执行情况会生成 JIT 日志,执行完成之后可以分析这个过程中的 JIT 日志。需要注意的是:这里必须达到了 JIT 的条件才会生成 JIT compile log,例如达到一定的调用次数。

以上面的 TestVolatile 为例,没有使用 volatile 关键字修饰 sum 变量:

jitwatch-sandbox1

使用 volatile 关键字修饰 sum 变量:

jitwatch-sandbox2

可以发现使用 volatile 关键字后多了一个 lock 指令:

0x0000000111cb8f5e: lock addl $0x0,(%rsp)  ;*putfield sum
                                           ; - TestVolatile::add@12 (line 6)
1
2

# 参考

  • https://github.com/funnycoding/blog/issues/40
  • https://hengyun.tech/jvm-hsdis-jitwatch/
#hsdis#jitwatch
上次更新: 2024-08-19
最近更新
01
开始
01-09
02
AI工具分享
01-09
03
AI 导读
01-07
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Jason Huang | 闽ICP备2025088096号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式