万隆的笔记 万隆的笔记
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
  • 大纲

  • 走近Java

  • 内存与垃圾回收

  • 字节码与类加载

    • JVM基石:class文件
    • class文件结构
    • javap解析class文件
      • 概述
      • javac -g 说明
      • javap的用法
      • 使用示例
      • 总结
    • 字节码指令集
    • 类加载过程(类生命周期)
    • 再谈类加载器
    • JVM指令手册
    • class字节码文件结构
  • 性能监控与调优

  • 监控与性能调优案例

  • Java虚拟机
  • 字节码与类加载
2022-03-12
目录

javap解析class文件

# javap解析class文件

# 概述

通过反编译生成字节码文件,我们可以深入了解java代码的工作机制,除了使用第三方jclasslib工具外,Oracle官方也提供了工具:javap。

javap是JDK自带的反解析工具,它的作用就是根据class字节码文件,反解析出当前类对应的code区(字节码码指令)、局部变量表、异常表和代码行偏移量映射表、常量池等信息。通过局部变量表,我们可以查看局部变量表的作用域范围、所在槽位等信息,甚至可以看到槽复用等信息。

# javac -g 说明

解析字节码文件得到的信息中,有些信息如局部变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等,需要使用javac -g编译成class文件,之后使用javap才能反编译输出。

也就说,如果我们直接javac xxx.java就不会生产对应的局部变量表等信息。默认情况下,Eclipse 或 IDEA,在编译时默认会帮我们生产局部变量表、指令和代码行偏移量映射表等信息的。

# javap的用法

javap的用法格式,其中classes就是你要反编译的class文件。

javap <options> <classes>

在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:

 -help  --help  -?        输出此用法消息
 -version                 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
 -v  -verbose             输出附加信息(包括行号、本地变量表,反汇编等详细信息)
 -l                       输出行号和本地变量表
 -public                  仅显示公共类和成员
 -protected               显示受保护的/公共类和成员
 -package                 显示程序包/受保护的/公共类 和成员 (默认)
 -p  -private             显示所有类和成员
 -c                       对代码进行反汇编,生产字节码指令
 -s                       输出内部类型签名
 -sysinfo                 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
 -constants               显示静态最终常量
 -classpath <path>        指定查找用户类文件的位置
 -bootclasspath <path>    覆盖引导类文件的位置

一般常用的是-v、-l、-c三个选项:

  • javap -v: 输出行号、本地变量表信息、反编译生成字节码指令,还会输出当前类用到的常量池等信息。
  • javap -l : 输出行号和本地变量表信息。
  • javap -c: 会对当前class字节码进行反编译生成字节码指令。

注意:

  1. -v相当于-c、-l
  2. -v也不会输出私有的字段、方法等信息,所以如果想输出私有的信息,那需要在-v后面加上-p才行。

# 使用示例

代码:

public class JavapTest {
    private int num;
    boolean flag;
    protected char gender;
    public String info;

    public static final int COUNTS = 1;
    static{
        String url = "www.atguigu.com";
    }
    {
        info = "java";
    }
    public JavapTest(){

    }
    private JavapTest(boolean flag){
        this.flag = flag;
    }
    private void methodPrivate(){

    }
    int getNum(int i){
        return num + i;
    }
    protected char showGender(){
        return gender;
    }
    public void showInfo(){
        int i = 10;
        System.out.println(info + i);
    }
}

执行javap指令:

javap -v -p JavapTest.class 

字节码文件:

Classfile /Users/wenwl/Projects/JVMDemo1/out/production/chapter01/com/atguigu/java1/JavapTest.class   // 字节码文件所属的路径 
  Last modified 2022年3月11日; size 1358 bytes   // 最后修改时间,字节码文件的大小 
  MD5 checksum 526b4a845e4d98180438e4c5781b7e88   // MD5散列值 
  Compiled from "JavapTest.java"   // 源文件的名称 
public class io.renren.JavapTest
  minor version: 0   // 副版本 
  major version: 52   // 主版本 
  flags: ACC_PUBLIC, ACC_SUPER   // 访问标识 
*************************** 常量池 ******************************** 
Constant pool:
   #1 = Methodref          #16.#48        // java/lang/Object."<init>":()V
   #2 = String             #49            // java
   #3 = Fieldref           #15.#50        // io/renren/JavapTest.info:Ljava/lang/String;
   #4 = Fieldref           #15.#51        // io/renren/JavapTest.flag:Z
   #5 = Fieldref           #15.#52        // io/renren/JavapTest.num:I
   #6 = Fieldref           #15.#53        // io/renren/JavapTest.gender:C
   #7 = Fieldref           #54.#55        // java/lang/System.out:Ljava/io/PrintStream;
   #8 = Class              #56            // java/lang/StringBuilder
   #9 = Methodref          #8.#48         // java/lang/StringBuilder."<init>":()V
  #10 = Methodref          #8.#57         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = Methodref          #8.#58         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #12 = Methodref          #8.#59         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #13 = Methodref          #60.#61        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #14 = String             #62            // www.atguigu.com
  #15 = Class              #63            // io/renren/JavapTest
  #16 = Class              #64            // java/lang/Object
  #17 = Utf8               num
  #18 = Utf8               I
  #19 = Utf8               flag
  #20 = Utf8               Z
  #21 = Utf8               gender
  #22 = Utf8               C
  #23 = Utf8               info
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               COUNTS
  #26 = Utf8               ConstantValue
  #27 = Integer            1
  #28 = Utf8               <init>
  #29 = Utf8               ()V
  #30 = Utf8               Code
  #31 = Utf8               LineNumberTable
  #32 = Utf8               LocalVariableTable
  #33 = Utf8               this
  #34 = Utf8               Lio/renren/JavapTest;
  #35 = Utf8               (Z)V
  #36 = Utf8               falg
  #37 = Utf8               MethodParameters
  #38 = Utf8               methodPrivate
  #39 = Utf8               getNum
  #40 = Utf8               (I)I
  #41 = Utf8               i
  #42 = Utf8               showGender
  #43 = Utf8               ()C
  #44 = Utf8               showInfo
  #45 = Utf8               <clinit>
  #46 = Utf8               SourceFile
  #47 = Utf8               JavapTest.java
  #48 = NameAndType        #28:#29        // "<init>":()V
  #49 = Utf8               java
  #50 = NameAndType        #23:#24        // info:Ljava/lang/String;
  #51 = NameAndType        #19:#20        // flag:Z
  #52 = NameAndType        #17:#18        // num:I
  #53 = NameAndType        #21:#22        // gender:C
  #54 = Class              #65            // java/lang/System
  #55 = NameAndType        #66:#67        // out:Ljava/io/PrintStream;
  #56 = Utf8               java/lang/StringBuilder
  #57 = NameAndType        #68:#69        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #58 = NameAndType        #68:#70        // append:(I)Ljava/lang/StringBuilder;
  #59 = NameAndType        #71:#72        // toString:()Ljava/lang/String;
  #60 = Class              #73            // java/io/PrintStream
  #61 = NameAndType        #74:#75        // println:(Ljava/lang/String;)V
  #62 = Utf8               www.atguigu.com
  #63 = Utf8               io/renren/JavapTest
  #64 = Utf8               java/lang/Object
  #65 = Utf8               java/lang/System
  #66 = Utf8               out
  #67 = Utf8               Ljava/io/PrintStream;
  #68 = Utf8               append
  #69 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #70 = Utf8               (I)Ljava/lang/StringBuilder;
  #71 = Utf8               toString
  #72 = Utf8               ()Ljava/lang/String;
  #73 = Utf8               java/io/PrintStream
  #74 = Utf8               println
  #75 = Utf8               (Ljava/lang/String;)V
****************************** 字段表集合的信息 ************************************** 
{
  private int num;   // 字段名 
    descriptor: I   // 字段表集合的信息 
    flags: ACC_PRIVATE   // 字段的访问标识 
 
  boolean flag;
    descriptor: Z
    flags:
 
  protected char gender;
    descriptor: C
    flags: ACC_PROTECTED
 
  public java.lang.String info;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC
 
  public static final int COUNTS;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 1   // 常量字段的属性:ConstantValue 
****************************** 方法表集合的信息 ************************************** 
  public io.renren.JavapTest();   // 无参构造器方法信息 
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String java
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 16: 0
        line 14: 4
        line 18: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lio/renren/JavapTest;
 
  private io.renren.JavapTest(boolean);   // 单个参数构造器方法信息 
    descriptor: (Z)V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String java
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: aload_0
        11: aload_0
        12: getfield      #4                  // Field flag:Z
        15: putfield      #4                  // Field flag:Z
        18: return
      LineNumberTable:
        line 19: 0
        line 14: 4
        line 20: 10
        line 21: 18
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      19     0  this   Lio/renren/JavapTest;
            0      19     1  falg   Z
    MethodParameters:
      Name                           Flags
      falg
 
  private void methodPrivate();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 24: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   Lio/renren/JavapTest;
 
  int getNum(int);
    descriptor: (I)I
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: getfield      #5                  // Field num:I
         4: iload_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 26: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lio/renren/JavapTest;
            0       7     1     i   I
    MethodParameters:
      Name                           Flags
      i
 
  protected char showGender();
    descriptor: ()C
    flags: ACC_PROTECTED
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #6                  // Field gender:C
         4: ireturn
      LineNumberTable:
        line 29: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lio/renren/JavapTest;
 
  public void showInfo();
    descriptor: ()V   // 方法的描述符:方法的形参列表、返回值类型 
    flags: ACC_PUBLIC   // 方法的访问标识 
    Code:   // 方法的Code属性 
      stack=3, locals=2, args_size=1   // stack:操作数栈的最大深度   locals:局部变量表的长度   args_size:方法接受参数的个数 
// 偏移量  操作码   操作数 
         0: bipush        100
         2: istore_1
         3: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #8                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        13: aload_0
        14: getfield      #3                  // Field info:Ljava/lang/String;
        17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: iload_1
        21: invokevirtual #11                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        24: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: return
     // 行号表:指明字节码指令的偏移量与java源代码中代码的行号的一一对应关系 
      LineNumberTable:
        line 32: 0
        line 33: 3
        line 34: 30
     // 局部变量表:描述内部局部变量的相关信息 
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  this   Lio/renren/JavapTest;
            3      28     1     i   I
 
  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=1, args_size=0
         0: ldc           #14                 // String www.atguigu.com
         2: astore_0
         3: return
      LineNumberTable:
        line 11: 0
        line 12: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
}
SourceFile: "JavapTest.java"   // 附加属性:指明当前字节码文件对应的源程序文件名 

# 总结

  1. javap命令可以查看一个Java类反汇编得到的Class文件版本号、常量池、访问标识、变量表、指令代码行号表等信息,不显示类索引、父类索引、接口索引集合、<clinit>()、<init>()等结构。
  2. 通过对上述例子反汇编文件简单分析发现,一个方法对执行通常会涉及下面几块内存操作:
    • java栈:局部变量表、操作数栈
    • java堆:通过对象的地址引用去操作
    • 常量池
    • 其他区域则没有显示出来。
  3. 平常我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档 (opens new window),查看每个指令的含义。
上次更新: 5/28/2023, 10:57:53 PM
字节码指令集

字节码指令集→

最近更新
01
2025
01-15
02
Elasticsearch面试题
07-17
03
Elasticsearch进阶
07-16
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式