[JVM] 用Javap分析字节码文件

Java源文件经过编译器编译会生成 .class文件(字节码),然后才能在JVM上运行。

javap是JDK自带的分析字节码文件的工具,它的用法如下。

 

F:\IdeaProject\HappyJava\src>javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?                输出此用法消息
  -version                         版本信息
  -v  -verbose                     输出附加信息
  -l                               输出行号和本地变量表
  -public                          仅显示公共类和成员
  -protected                       显示受保护的/公共类和成员
  -package                         显示程序包/受保护的/公共类
                                   和成员 (默认)
  -p  -private                     显示所有类和成员
  -c                               对代码进行反汇编
  -s                               输出内部类型签名
  -sysinfo                         显示正在处理的类的
                                   系统信息 (路径, 大小, 日期, MD5 散列)
  -constants                       显示最终常量
  --module <模块>, -m <模块>       指定包含要反汇编的类的模块
  --module-path <路径>             指定查找应用程序模块的位置
  --system <jdk>                   指定查找系统模块的位置
  --class-path <路径>              指定查找用户类文件的位置
  -classpath <路径>                指定查找用户类文件的位置
  -cp <路径>                       指定查找用户类文件的位置
  -bootclasspath <路径>            覆盖引导类文件的位置

 

以一个普通的Java类为例:

 1 public class ClassStructure
 2 {
 3     private static final double _e = 2.73;
 4 
 5     private String name;
 6     public int age;
 7     public String[] hobbies;
 8 
 9     public static void sayHi()
10     {
11         System.out.println("Hi, friend!");
12     }
13 
14     public void printNum()
15     {
16         for (int i = 0; i < 10; i++) {
17             for (int j = 0; j < 15; j++) {
18                 System.out.println("xyz");
19             }
20         }
21     }
22 }

首先,将其编译为Class文件,-g:vars 的作用是将方法内的局部变量表的名称也生成到Class文件中。否则IDE会用arg0, arg1之类的占位符代替原有的参数名,不便于调试。

F:\IdeaProject\HappyJava\src>javac -g:vars jvm/classStructure/ClassStructure.java

接下来,用 javap 分析字节码,它的主要几个用法为:

1. -v 或者 -verbose 输出附加信息,包括常量池,反编译的信息等,涵盖了大部分信息。

2. -c 输出反编译的结果

3. -p 或者 -private 显示所有类和成员(包括 private 修饰的)

先用 -p ,得到所有公有私有成员:

F:\IdeaProject\HappyJava\src>javap -p jvm.classStructure.ClassStructure
public class jvm.classStructure.ClassStructure {
  private static final double _e;
  private java.lang.String name;
  public int age;
  public java.lang.String[] hobbies;
  public jvm.classStructure.ClassStructure();
  public static void sayHi();
  public void printNum();
}

再用 -c 反汇编,关键是看懂各个助记符的意思,跟x86汇编语言意思差不多,内容太多,可以看官网文档或者《深入理解Java虚拟机》末尾的附录。比如:

aload_0: 是把第1个引用类型本地变量推送至栈顶。这里是对象自身的引用this。

invokespecia: 调用超类构造方法/实例初始化方法/私有方法。这里调用超类Object的init(),因为没有覆盖init()方法。

invokevirtual: 调用实例方法,最常见的一种。/

getstatic: 获取指定类的静态域,并将其值压入栈顶。这里是System.out。

ldc: 将int,float,String型常量从常量值中推送至栈顶。

bipush  n:将单字节常量值n(-128~127)推送至栈顶。bipush 10; bipush 15是因为 i 和 j 分别循环10次和15次。

if_icmpge row_num: if greater or equal than 0, then jump to Row row_num.

iinc: 自增操作。

goto: 跳转。

语句旁边的#1,#2等各自对应的是什么在常量池里(Constant pool),用 -v 可以查看到。

F:\IdeaProject\HappyJava\src>javap -c jvm.classStructure.ClassStructure
public class jvm.classStructure.ClassStructure {
  public int age;

  public java.lang.String[] hobbies;

  public jvm.classStructure.ClassStructure();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void sayHi();
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hi, friend!
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public void printNum();
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: bipush        10
       5: if_icmpge     36
       8: iconst_0
       9: istore_2
      10: iload_2
      11: bipush        15
      13: if_icmpge     30
      16: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      19: ldc           #5                  // String xyz
      21: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      24: iinc          2, 1
      27: goto          10
      30: iinc          1, 1
      33: goto          2
      36: return
}

 如果用 -v 可以获得基本所有信息,开头是常量池,然后是属性和方法。每行常量最前面都是编号(#1,#2等),之后的方法里引用这些常量的时候就用编号。

F:\IdeaProject\HappyJava\src>javap -v -p jvm.classStructure.ClassStructure
Classfile /F:/IdeaProject/HappyJava/src/jvm/classStructure/ClassStructure.class
  Last modified 2019年1月23日; size 734 bytes
  MD5 checksum 9c618383b59ffc86b1d830b09d4f0788
public class jvm.classStructure.ClassStructure
  minor version: 0
  major version: 54
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #6                          // jvm/classStructure/ClassStructure
  super_class: #7                         // java/lang/Object
  interfaces: 0, fields: 4, methods: 3, attributes: 0
//常量池
Constant pool:
#
1 = Methodref #7.#30 // java/lang/Object."<init>":()V #2 = Fieldref #31.#32 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #33 // Hi, friend! #4 = Methodref #34.#35 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = String #36 // xyz #6 = Class #37 // jvm/classStructure/ClassStructure #7 = Class #38 // java/lang/Object #8 = Utf8 _e #9 = Utf8 D #10 = Utf8 ConstantValue #11 = Double 2.73d #13 = Utf8 name #14 = Utf8 Ljava/lang/String; #15 = Utf8 age #16 = Utf8 I #17 = Utf8 hobbies #18 = Utf8 [Ljava/lang/String; #19 = Utf8 <init> #20 = Utf8 ()V #21 = Utf8 Code #22 = Utf8 LocalVariableTable #23 = Utf8 this #24 = Utf8 Ljvm/classStructure/ClassStructure; #25 = Utf8 sayHi #26 = Utf8 printNum #27 = Utf8 j #28 = Utf8 i #29 = Utf8 StackMapTable #30 = NameAndType #19:#20 // "<init>":()V #31 = Class #39 // java/lang/System #32 = NameAndType #40:#41 // out:Ljava/io/PrintStream; #33 = Utf8 Hi, friend! #34 = Class #42 // java/io/PrintStream #35 = NameAndType #43:#44 // println:(Ljava/lang/String;)V #36 = Utf8 xyz #37 = Utf8 jvm/classStructure/ClassStructure #38 = Utf8 java/lang/Object #39 = Utf8 java/lang/System #40 = Utf8 out #41 = Utf8 Ljava/io/PrintStream; #42 = Utf8 java/io/PrintStream #43 = Utf8 println #44 = Utf8 (Ljava/lang/String;)V {
  //属性 private static final
double _e; descriptor: D flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL ConstantValue: double 2.73d private java.lang.String name; descriptor: Ljava/lang/String; flags: (0x0002) ACC_PRIVATE public int age; descriptor: I flags: (0x0001) ACC_PUBLIC public java.lang.String[] hobbies; descriptor: [Ljava/lang/String; flags: (0x0001) ACC_PUBLIC   //方法 public jvm.classStructure.ClassStructure(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code:
    //这里的locals=1和args_size=1是因为编译器传了this这个参数给方法 stack
=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Ljvm/classStructure/ClassStructure; public static void sayHi(); descriptor: ()V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code:
    //因为是静态方法,所以编译器不会传this进去,所以locals=0,args_size=0 stack
=2, locals=0, args_size=0 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hi, friend! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return public void printNum(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 36 8: iconst_0 9: istore_2 10: iload_2 11: bipush 15 13: if_icmpge 30 16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 19: ldc #5 // String xyz 21: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 24: iinc 2, 1 27: goto 10 30: iinc 1, 1 33: goto 2 36: return
    
    //局部变量表,显示了局部变量和它们代号之间的对应关系 LocalVariableTable: Start Length Slot Name Signature
10 20 2 j I 2 34 1 i I 0 37 0 this Ljvm/classStructure/ClassStructure; StackMapTable: number_of_entries = 4 frame_type = 252 /* append */ offset_delta = 2 locals = [ int ] frame_type = 252 /* append */ offset_delta = 7 locals = [ int ] frame_type = 250 /* chop */ offset_delta = 19 frame_type = 250 /* chop */ offset_delta = 5 }

 

转载于:https://www.cnblogs.com/wanyi/p/10311065.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值