Android字节码处理-字节码包含的相关信息

158 阅读3分钟

字节码

.class文件是按照字节码标准形成的字节码文件。javap是Java class文件分解器,可以反编译,也可以查看java编译器生成的字节码。用于分解class文件。

Java源码

public class InvokeTest {
    int i = 0;
    public void invokeTest() {

    }
    public synchronized void test() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void test2() {
        synchronized (this) {
            test3();
            i++;
        }
    }

    private void test3() {

    }

    public static void main(String[] args) {

    }
}

查看字节码

java命令中我经常使用的命令是javap -p -v 类名,例如:javap -p -v InvokeTest.class,结果如下所示:

Last modified 2025年3月23日; size 1076 bytes        Test\$1.class              Test.class                 
  SHA-256 checksum 14a3ce22deb3e6be6e9b1ccffdfe19e58c3b821710ae1d916751a71d199d597e
  Compiled from "InvokeTest.java"
public class InvokeTest
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #10                         // InvokeTest
  super_class: #11                        // java/lang/Object
  interfaces: 0, fields: 1, methods: 6, attributes: 1
Constant pool:
   #1 = Methodref          #11.#35        // java/lang/Object."<init>":()V
   #2 = Fieldref           #10.#36        // InvokeTest.i:I
   #3 = Long               1000l
   #5 = Methodref          #37.#38        // java/lang/Thread.sleep:(J)V
   #6 = Class              #39            // java/lang/InterruptedException
   #7 = Class              #40            // java/lang/RuntimeException
   #8 = Methodref          #7.#41         // java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
   #9 = Methodref          #10.#42        // InvokeTest.test3:()V
  #10 = Class              #43            // InvokeTest
  #11 = Class              #44            // java/lang/Object
  #12 = Utf8               i
  #13 = Utf8               I
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               LInvokeTest;
  #21 = Utf8               invokeTest
  #22 = Utf8               test
  #23 = Utf8               e
  #24 = Utf8               Ljava/lang/InterruptedException;
  #25 = Utf8               StackMapTable
  #26 = Utf8               test2
  #27 = Class              #45            // java/lang/Throwable
  #28 = Utf8               test3
  #29 = Utf8               main
  #30 = Utf8               ([Ljava/lang/String;)V
  #31 = Utf8               args
  #32 = Utf8               [Ljava/lang/String;
  #33 = Utf8               SourceFile
  #34 = Utf8               InvokeTest.java
  #35 = NameAndType        #14:#15        // "<init>":()V
  #36 = NameAndType        #12:#13        // i:I
  #37 = Class              #46            // java/lang/Thread
  #38 = NameAndType        #47:#48        // sleep:(J)V
  #39 = Utf8               java/lang/InterruptedException
  #40 = Utf8               java/lang/RuntimeException
  #41 = NameAndType        #14:#49        // "<init>":(Ljava/lang/Throwable;)V
  #42 = NameAndType        #28:#15        // test3:()V
  #43 = Utf8               InvokeTest
  #44 = Utf8               java/lang/Object
  #45 = Utf8               java/lang/Throwable
  #46 = Utf8               java/lang/Thread
  #47 = Utf8               sleep
  #48 = Utf8               (J)V
  #49 = Utf8               (Ljava/lang/Throwable;)V
{
  int i;
    descriptor: I
    flags: (0x0000)

  public InvokeTest();
    descriptor: ()V
    flags: (0x0001) 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: iconst_0
         6: putfield      #2                  // Field i:I
         9: return
      LineNumberTable:
        line 1: 0
        line 2: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   LInvokeTest;

  public void invokeTest();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   LInvokeTest;

  public synchronized void test();
    descriptor: ()V
    flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=2, args_size=1
         0: ldc2_w        #3                  // long 1000l
         3: invokestatic  #5                  // Method java/lang/Thread.sleep:(J)V
         6: goto          19
         9: astore_1
        10: new           #7                  // class java/lang/RuntimeException
        13: dup
        14: aload_1
        15: invokespecial #8                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
        18: athrow
        19: return
      Exception table:
         from    to  target type
             0     6     9   Class java/lang/InterruptedException
      LineNumberTable:
        line 8: 0
        line 11: 6
        line 9: 9
        line 10: 10
        line 12: 19
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           10       9     1     e   Ljava/lang/InterruptedException;
            0      20     0  this   LInvokeTest;
      StackMapTable: number_of_entries = 2
        frame_type = 73 /* same_locals_1_stack_item */
          stack = [ class java/lang/InterruptedException ]
        frame_type = 9 /* same */

  public void test2();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_0
         5: invokevirtual #9                  // Method test3:()V
         8: aload_0
         9: dup
        10: getfield      #2                  // Field i:I
        13: iconst_1
        14: iadd
        15: putfield      #2                  // Field i:I
        18: aload_1
        19: monitorexit
        20: goto          28
        23: astore_2
        24: aload_1
        25: monitorexit
        26: aload_2
        27: athrow
        28: return
      Exception table:
         from    to  target type
             4    20    23   any
            23    26    23   any
      LineNumberTable:
        line 15: 0
        line 16: 4
        line 17: 8
        line 18: 18
        line 19: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   LInvokeTest;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 23
          locals = [ class InvokeTest, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  private void test3();
    descriptor: ()V
    flags: (0x0002) ACC_PRIVATE
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 23: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   LInvokeTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 27: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;
}
SourceFile: "InvokeTest.java"

完整解析上面的字节码如下: 1.Last modified 2025年3月23日; size 1076 bytes Test$1.class 这行信息代表最后更改时间。 2. major version: 55,代表JAVA11,52代表JAVA8,53代表JAVA9,54代表JAVA10,以此类推。 3. flags: (0x0021) ACC_PUBLIC, ACC_SUPER代表class是public。 4. super_class: #11 // java/lang/Object,代表父类是Object,#11代表常量池的索引。 5. interfaces: 0, fields: 1, methods: 6, attributes: 1代表实现的接口为0个,field(实力变量)为1个,方法6个,属性1个。 6. 常量池列表中包括class,例如:#11 = Class #44 // java/lang/Object,方法例如:#1 = Methodref #11.#35 // java/lang/Object."":()V,变量例如:#3 = Long 1000l。

7.int i; 描述为I,就是int的描述。

  1. public InvokeTest();构造方法解析:描述符为: ()V,无参数返回值为空。标志位为public。代码stack=2, locals=1, args_size=1,操作数栈最大值为2,局部变量为1,参数个数为1(实例方法参数个数加1,静态方法为参数个数)。 0: aload_0 //把实例从局部变量索引的0位置存放到操作数栈的顶部

    1: invokespecial #1 // Method java/lang/Object."":()V 调用Object的init方法

    4: aload_0 //把实例从局部变量索引的0位置存放到操作数栈的顶部

    5: iconst_0 //把int值0放到操作数顶部

    6: putfield #2 // Field i:I 把实例属性i设置为0,操作数栈顶部数据分别是0和刚刚构建的实例

    7: return //方法返回

9.public synchronized void test()方法描述()V无参数无返回值,标志位包括ACC_SYNCHRONIZED,代表同步方法,多线程同时调用此方法的时候,只能有一个线程能获取锁,方法执行完毕之后,线程会释放锁。

方法调用和返回指令的区别

在字节码指令中,与方法调用相关的指令有如下5种:

  • invokestatic:该指令用于调用静态方法。
  • invokeinterface:该指令用于调用接口方法。
  • invokespecial:该指令用于调用一些需要特殊处理的实例方法,包括实例的初始化方法、私有方法和父类方法。
  • invokevirtual:该指令用于调用对象的实例方法。
  • invokedynamic:该指令用于在运行时动态解析出调用点限定符所用的方法,并执行该方法。

总结

字节码文件中的信息方法名,变量名,以及方法体的具体实现,不同方法调用之间的异同点,以及方法局部变量和操作数栈之间的转换等详细信息。希望文章对您有所帮助,如有错误,请不吝指出。