0%

javap命令

Java javap 命令:解析字节码的利器

javap 是 JDK 自带的字节码分析工具,用于将 .class 文件反编译为人类可读的字节码指令、常量池、类结构等信息。它是理解 Java 代码底层实现、排查性能问题、学习 JVM 工作原理的重要工具。本文将详细介绍 javap 的用法、常用选项及输出内容解析。

javap 基本用法

语法格式

1
javap [选项] <class文件或类名>
  • <class文件或类名>:可以是 .class 文件路径(如 Test.class),或类的全限定名(如 com.example.Test,需确保类在类路径下)。

常用选项

javap 提供多个选项,用于控制输出内容的详细程度,核心选项如下:

选项 作用描述
-v-verbose 输出最详细信息,包括常量池、字节码指令、行号表、局部变量表等。
-c 对方法进行反汇编,输出字节码指令(最常用选项)。
-l 输出行号表和局部变量表(配合 -c-v 使用)。
-p-private 显示所有类成员(包括 private 修饰的方法和字段,默认只显示 public/protected)。
-constants 输出常量池中的常量(如字符串、整数等)。
-sysinfo 显示类文件的系统信息(路径、大小、修改时间、MD5 哈希)。

javap 输出内容解析

以示例类 TestString.class 为例,使用 javap -v TestString.class 输出的内容可分为以下几个核心部分:

类基本信息

输出开头显示类的版本、访问标志等基础信息:

1
2
3
4
public class com.zhanghe.study.string.TestString
minor version: 0 // 副版本号
major version: 52 // 主版本号(52 对应 JDK 8)
flags: ACC_PUBLIC, ACC_SUPER // 访问标志(public 类,继承自 Object)
  • 版本号major version 对应 JDK 版本(如 52=JDK8,55=JDK11),用于判断类的编译版本。
  • 访问标志:描述类的属性(如 ACC_PUBLIC 表示公开类,ACC_FINAL 表示最终类)。

常量池(Constant Pool)

常量池是字节码文件的 “字典”,存储类中使用的常量、符号引用(类名、方法名、字段名等),格式为:

1
2
3
4
5
6
Constant pool:
#1 = Methodref #17.#43 // java/lang/Object."<init>":()V
#2 = Class #44 // java/lang/String
#3 = String #45 // zxc
#4 = Methodref #2.#46 // java/lang/String."<init>":(Ljava/lang/String;)V
...
  • 每个常量以 #N 编号,类型包括 Methodref(方法引用)、Class(类引用)、String(字符串常量)等。
  • 常量池是理解方法调用、字段访问的关键(如 #1 表示 Object 类的无参构造器)。

类成员(方法和字段)

javap 会列出类中的所有方法(包括构造器),并显示其字节码指令、局部变量表等信息。以 main 方法为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V // 方法描述符(参数为 String[],返回值 void)
flags: ACC_PUBLIC, ACC_STATIC // 访问标志(公开静态方法)
Code:
stack=3, locals=8, args_size=1 // 操作数栈最大深度=3,局部变量表大小=8,参数个数=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String zxc
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
...
LineNumberTable: // 字节码行号与源码行号的映射
line 10: 0
line 11: 10
...
LocalVariableTable: // 局部变量表(变量名、类型、作用范围)
Start Length Slot Name Signature
0 168 0 args [Ljava/lang/String;
10 158 1 s Ljava/lang/String;
...
  • Code 区块:方法的字节码指令(如 new 创建对象、ldc 加载常量、invokespecial 调用构造器)。
  • LineNumberTable:用于调试时将字节码指令映射到源码行号(如 line 10: 0 表示源码第 10 行对应字节码第 0 条指令)。
  • LocalVariableTable:记录局部变量的信息(如 argsString[] 类型,存储在 Slot 0)。

其他信息

  • StackMapTable:用于 JVM 验证字节码的合法性,记录方法执行过程中的栈和局部变量表状态(主要供 JVM 内部使用)。
  • SourceFile:显示源码文件名(如 SourceFile: "TestString.java")。

典型使用场景

分析方法调用底层实现

例如,查看字符串拼接的字节码,验证 s1 + s2 是否通过 StringBuilder 实现:

1
javap -c TestString.class | grep -A 10 "s4 ="

输出会显示 new StringBuilder()appendtoString 等指令,证实拼接底层依赖 StringBuilder

验证变量作用域和类型

通过 LocalVariableTable 可查看局部变量的类型和有效范围,例如:

1
2
Start  Length  Slot  Name   Signature
21 147 2 s1 Ljava/lang/String;

表示 s1String 类型,从字节码第 21 条指令开始有效,共 147 条指令长度。

调试异常或性能问题

通过字节码指令序列,可定位异常发生的具体位置(如 athrow 指令对应 throw 语句),或分析循环、分支的执行逻辑(如 if_acmpne 条件跳转)。

常用命令组合

  1. 查看方法字节码(最常用)

    1
    javap -c Test.class  # 反汇编所有方法的字节码
  2. 查看详细信息(包括常量池和局部变量)

    1
    javap -v Test.class  # 输出最完整的信息
  3. 查看私有成员

    1
    javap -p -c Test.class  # 显示 private 方法和字段的字节码
  4. 仅查看常量池

    1
    javap -constants Test.class  # 输出常量池中的常量

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10