> 文章列表 > 一起来学ASM字节码插桩:从分析class文件结构开始

一起来学ASM字节码插桩:从分析class文件结构开始

一起来学ASM字节码插桩:从分析class文件结构开始

文章目录

      • Class字节
        • class字节码构成
      • 类型描述符
        • 基本类型描述符
        • 非数组的引用类型
        • 数组引用类型
      • 方法描述符
      • OpCode 操作码

Class字节码

Java 能做到 一次编译,到处运行,主要就是靠 class字节码 文件,也就是 java 文件经过编译之后 .java -> .class,然后再被 JVM 虚拟机加载。其实,不仅是 java 语言,只要是符合规则的 class 字节码文件,都可以被 JVM 加载,如 Grooy、Kolin 语言:
一起来学ASM字节码插桩:从分析class文件结构开始

有了 class 字节码,也就解除了 VM虚拟机编程语言之间的耦合性。不管什么语言,只要是能编译成符合规则的字节码文件,就可以被 VM虚拟机 识别并加载到内存中

class字节码构成

class字节码 中由无符号数两种数据结构组成。

  • 无符号数:基本数据类型,u1、u2、u4、u8分别表示1、2、4、8个字节的无符号数,无符号数可以用来表示数字、索引引用、数量只或者字符串(UTF-8编码)。
  • 表:由多个无符号数或者其他表作为数据项构成的复合数据类型,class文件中所有的表都以“_info”结尾。所以,整个 Class 文件可以认为就是一张表。
package org.ninetripods.lib_bytecode.asm.coreApi;public class Test {private int num = 0;public void addNum() {num++;System.out.println("num:" + num);}public static int staticAdd(int a, int b) {return a + b;}
}

经过javac编译之后,会生成Test.class字节码文件,用010 Editor打开这个文件:

一起来学ASM字节码插桩:从分析class文件结构开始
使用 javap -c xxx.class命令可以 输出分解后的 java字节码

public class org.ninetripods.lib_bytecode.asm.coreApi.Test {public org.ninetripods.lib_bytecode.asm.coreApi.Test();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: iconst_06: putfield      #2                  // Field num:I9: returnpublic void addNum();Code:0: aload_01: dup2: getfield      #2                  // Field num:I5: iconst_16: iadd7: putfield      #2                  // Field num:I10: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;13: aload_014: getfield      #2                  // Field num:I17: invokedynamic #4,  0              // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;22: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V25: returnpublic static int staticAdd(int, int);Code:0: iload_01: iload_12: iadd3: ireturn
}

一起来学ASM字节码插桩:从分析class文件结构开始
上述文件中描述了Test.class加载到内存时的执行顺序,包括各种描述符及操作码,接下面就来看一下。

类型描述符

基本类型描述符

每个基本类型都对应一个字母,如下:

基本类型 描述符
byte B
short S
int I
long J
float F
double D
char C
boolean Z
void V

非数组的引用类型

L + 全限定名,示例:

注:全限定名用于描述class类的名称,实际上就是把平时Java类名称中的"."换成了"/",如Java的祖先类java.lang.Object全限定名是:java/lang/Object

数组引用类型

[ + 数组内类型的描述符,示例:

方法描述符

方法描述符用于class字节码文件中保存参数类型列表和返回值的方式。
方法描述符规则:

  • (参数列表)+ 返回值
  • 参数类型都为类型描述符
  • 参数列表中如果有多个参数,直接排列即可,不需要用逗号隔开

示例:

void test() -> ()V
void test(int i) -> (I)V
void test(String s, int i) -> (Ljava/lang/StringI)Vint[] test(double[], boolean) -> ([DZ)[I

OpCode 操作码

OpCode用于VM虚拟机解释运行Java程序,每个操作码都可以用来表示一个指令。如0x62是一个十六进制数,表示两个float类型的数相加。在ASM的org.objectweb.asm.OpCodes类中可以找到: int FADD = 98,ASM中是用十进制表示的,除此之外,其他操作码指令都可以在这个类中找到。

ISO9000网