1. javac
作用
默认情况下,javac只生成行号和源文件信息(相当于 -g:lines,source)。
加上 -g后,会额外包含局部变量的调试信息 ,方便在 IDE 或调试器中查看变量值、堆栈状态等。
常用变体
选项
含义
-g
生成所有调试信息(lines + vars + source)
-g:none
不生成任何调试信息
-g:lines
仅生成行号信息
-g:vars
仅生成局部变量信息
-g:source
仅生成源文件信息
2. javap 基本帮助与版本
-help/ -?/ --help 输出此用法消息
-version 显示 javap 所在 JDK 的版本信息(非 class 文件版本)
访问级别控制(显示哪些成员)
-public 仅显示公共类和成员
-protected 显示受保护的及公共类和成员
-p ||-private 显示所有类和成员(包括私有)
-package 显示程序包、受保护的及公共类和成员(默认行为)
输出内容详细程度
-sysinfo 显示正在处理的类的系统信息(路径、大小、日期、MD5 散列、源文件名)
-constants 显示静态 final 常量(显示值)
-s 输出内部类型签名(即 JVM 内部类型描述符)
-l 输出行号和局部变量表
-c 对字节码进行反汇编(输出 JVM 指令)
-v ||-verbose 输出附加详细信息(包含行号、局部变量表、反汇编等全部信息)
最全指令
测试类:
public class Hello { public static final String GREETING = "Hello, World!" ; private int count; protected double value; public Hello (int count) { this .count = count; this .value = 0.0 ; } private void increment () { count++; } public void sayHello (String name) { System.out.println(GREETING + ", " + name); increment(); } static void reset () { System.out.println("Reset called" ); } }
3. 解析 javap 输出 以下逐段解析 javap -v -p Hello 的输出。
3.1 类头部信息 Classfile /path/to/Hello.class Last modified 2026-07-10; size 1234 bytes MD5 checksum aabbccddee... Compiled from "Hello.java" public class Hello minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER
Classfile :.class文件的路径。
Last modified / size / MD5 :文件的修改时间、大小、MD5 校验值(-sysinfo也会输出)。
Compiled from :源文件名。
public class Hello :类声明。
minor/major version :编译出的 class 文件格式版本。major 61 对应 Java 17。
flags :类的访问标志。ACC_PUBLIC表示类是公有的,ACC_SUPER表示该类使用 invokespecial 指令的新语义(JDK 1.0.2 之后默认都有)。
3.2 常量池(Constant Pool) Constant pool: #1 = Methodref #7.#22 // java/lang/Object."<init>":()V #2 = Fieldref #6.#23 // Hello.count:I #3 = Fieldref #6.#24 // Hello.value:D #4 = String #25 // Hello, World! #5 = Fieldref #6.#26 // Hello.GREETING:Ljava/lang/String; ...
常量池是 class 文件的资源仓库,存储了所有符号引用(类、方法、字段、字符串字面量等)。每条记录有一个编号(#1, #2…),后面跟着类型和具体内容。javap会自动解析并显示注释(//后面的内容)。
常见条目:
常量池是所有后续指令的基础,字节码中的操作数往往直接引用常量池索引。
3.3 类的基本信息(this_class, super_class, interfaces) { Hello: this_class = #6 // Hello super_class = #7 // java/lang/Object interfaces: 0 items }
this_class :当前类在常量池中的索引(指向 Class 条目)。
super_class :父类索引。
interfaces :实现的接口数量及列表。
3.4 字段(Fields) private int count; descriptor: I flags: (0x0002) ACC_PRIVATE protected double value; descriptor: D flags: (0x0004) ACC_PROTECTED public static final java.lang.String GREETING; descriptor: Ljava/lang/String; flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL ConstantValue: String #4 // Hello, World!
descriptor :JVM 内部类型描述符。I=int, D=double, Ljava/lang/String;=String 类。
flags :字段的访问修饰符组合。
ConstantValue :如果是 static final字段且值为常量,则直接在这里显示常量池索引。
3.5 方法(Methods) 5.1 构造器 <init>
public Hello(int); descriptor: (I)V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iload_1 6: putfield #2 // Field count:I 9: aload_0 10: dconst_0 11: putfield #3 // Field value:D 14: return LineNumberTable: line 13: 0 line 15: 4 line 16: 9 line 18: 14 LocalVariableTable: Start Length Slot Name Signature 0 15 0 this LHello; 0 15 1 count I
descriptor :(I)V表示接收一个 int 参数,返回 void。
Code :方法体字节码。
stack :操作数栈最大深度。
locals :局部变量个数(包括 this)。
args_size :参数个数(包括 this对实例方法而言)。
字节码指令序列:每行 偏移: 指令 [操作数] // 注释。
LineNumberTable :字节码偏移量与源代码行号的映射(调试用)。
LocalVariableTable :局部变量名、类型、作用域范围(调试用)。
5.2 私有方法 increment
private void increment(); descriptor: ()V flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field count:I 5: iconst_1 6: iadd 7: putfield #2 // Field count:I 10: return LineNumberTable: line 21: 0 line 22: 10
因为 -p选项,私有方法也被显示出来。
5.3 公有方法 sayHello
public void sayHello(java.lang.String); descriptor: (Ljava/lang/String;)V flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #27 // class java/lang/StringBuilder 6: dup 7: invokespecial #28 // Method java/lang/StringBuilder."<init>":()V 10: getstatic #5 // Field Hello.GREETING:Ljava/lang/String; 13: invokevirtual #29 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: ldc #30 // String ", " 18: invokevirtual #29 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: aload_1 22: invokevirtual #29 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: invokevirtual #31 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 28: invokevirtual #32 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 31: aload_0 32: invokevirtual #33 // Method Hello.increment:()V 35: return LineNumberTable: line 25: 0 line 26: 31 line 27: 35
这里可以看到编译器对字符串拼接优化成了 StringBuilder。
5.4 包私有静态方法 reset
static void reset(); descriptor: ()V flags: (0x0008) ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #34 // String Reset called 5: invokevirtual #32 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 30: 0 line 31: 8
因为没有 this,locals=0(实际上局部变量表为空)。
3.6 内部类(InnerClasses) InnerClasses: public #36= #19 of #6; // Inner=class Hello$Inner of class Hello
这部分列出了内部类的关系。#36是内部类信息在常量池中的索引,#19是内部类本身的 Class 条目,#6是外部类 Hello 的 Class 条目。public表示内部类的访问标志。
如果内部类还有更多细节(如匿名内部类),也会在此显示。
其他可能的区域(视类情况而定)
Exceptions :方法抛出的受检异常列表。
AnnotationDefault :注解类型的默认值。
BootstrapMethods :invokedynamic 指令引导方法表(lambda 表达式、字符串拼接等)。
NestMembers / NestHost :Java 11 引入的嵌套类信息。