printassembly 默认不工作是因为jdk官方发行版缺少hsdis反汇编器库,需手动添加对应系统文件并配合-xx:+unlockdiagnosticvmoptions启用,且仅对jit编译的方法输出有效汇编。

PrintAssembly 为什么默认不工作
因为 PrintAssembly 不是开箱即用的功能,它依赖外部反汇编器 hsdis(HotSpot Disassembler),而 JDK 官方发行版不自带这个库。你执行 java -XX:+PrintAssembly -version 看到 Could not load hsdis-amd64.dylib 或类似提示,就说明它根本没加载成功——不是配置错,是压根缺文件。
不同系统要的文件名不同:hsdis-amd64.so(Linux)、hsdis-amd64.dylib(macOS)、hsdis-amd64.dll(Windows)。JDK 17+ 的某些构建(如 Liberica、Eclipse Temurin)已内置,但 Oracle/OpenJDK 官方包仍需手动补全。
- 别试图从旧 JDK 拷贝
hsdis到新 JDK —— 架构(amd64 vs aarch64)、JVM 版本(JDK 8 vs JDK 21)不匹配会导致library not loadable -
-XX:+UnlockDiagnosticVMOptions必须加,否则PrintAssembly被静默忽略(连 warning 都不报) - 只对 JIT 实际编译的方法输出汇编;解释执行或未达编译阈值的代码不会出现
怎么让 PrintAssembly 输出真正有用的汇编
光有 hsdis 还不够。默认输出极冗长,且混杂大量调试符号、注释和非热点代码,实际看汇编时容易迷失重点。
- 加
-XX:+PrintInlining对照看:内联是否发生,直接决定你看到的是单个方法体,还是被展开后的“扁平化”指令流 - 用
-XX:CompileCommand=compileonly,*YourClass.yourMethod锁定目标方法,避免日志被无关编译冲刷掉 - 重定向输出:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly YourApp 2> asm.log,再用grep -A5 -B5 "yourMethod\|mov\|add\|cmp"快速定位关键指令段 - 避免在生产环境启用:
PrintAssembly会显著拖慢 JIT 编译速度,且输出量可能达 GB 级
从汇编里能看出哪些 JIT 优化效果
真正有价值的观察点,不是“有没有汇编”,而是“汇编是否变短、变直、变少跳转”。比如一个简单循环:
public static int sum(int[] arr) {
int s = 0;
for (int i = 0; i
<p>优化到位的汇编里,你大概率看不到 <code>cmp</code> + <code>jl</code>(带符号比较跳转)这种通用循环结构,而是:</p>
- 数组边界检查被消除 → 没有
test/jl检查i - 循环展开 → 多个
mov+add连续出现,而非单次迭代指令重复 - 向量化痕迹 → 出现
paddd、pmovmskb等 SSE/AVX 指令(需开启-XX:+UseSuperWord) - 内联后无
call→ 原本arr[i]的getarray字节码变成直接内存寻址mov eax, dword ptr [rdx+rax*4+16h]
如果看到大量 call、test、jmp,说明 JIT 没能证明安全,退回到了保守模式。
PrintAssembly 日志里最常被误读的三件事
很多人扫一眼汇编就下结论,结果和实际性能走向相反。
-
Compiled method (c2)不代表优化好:C2 编译只是层级高,但如果方法太大(too big)、有异常处理块、或含反射调用,它照样生成臃肿低效的机器码 - 看到
mov多 ≠ 性能差:现代 CPU 的 mov 指令常被消除(mov-elimination),真正瓶颈可能是 cache miss 或分支预测失败,得结合-XX:+PrintGCDetails和 perf 工具交叉验证 - 汇编里没看到你的方法?不一定是没编译——可能被逃逸分析优化掉了对象分配,或被死代码消除(DCE),此时应配合
-XX:+PrintEscapeAnalysis查看
真正难的不是打出汇编,而是把一行 add eax, dword ptr [r8+r9*4+10h] 和你写的那行 Java 逻辑、JVM 的编译决策、CPU 的微架构行为串起来看。漏掉任意一环,就容易把优化当成退化,或者把退化当成优化。










