认识class文件
二进制字节流
硬盘上的文件都是以二进制流的方式存储的
类文件结构
- Magic Number 魔数 文件类型, .class的16进制魔数就是cafebabe
- minor version+major version 小大版本号 jdk8对应的版本号是52,其16进制是0034
- constant_poll_count 常量池中常量数量
- constant_poll 常量池是一个长度=constant_poll_count-1的列表,保留了空的0位
- 权限标识
- 类名 、父类
- 接口数量 所有接口
- 成员变量总数 成员变量
- 方法总数 方法
-
演示
8 public class ClassViewer {
9 private int c;
10 public String add(String a){
11 int i =0 ,j=10;
12 i++;
13 c += j;
14 return a+i+j;
15 }
16}
16进制
Javap -v ClassViewer.class
下述class类信息块 所说的栈 和 栈顶,不做特殊说明 都指的是操作数栈
Last modified 2021-3-30; size 558 bytes
MD5 checksum 9007716478deccca8618ff428c3c57dc
Compiled from "ClassViewer.java"
public class com.?.ClassViewer
minor version: 0
major version: 52 //jdk的MarjorVersion=52
flags: ACC_PUBLIC, ACC_SUPER // 访问权限修饰符
Constant pool:
#1 = Methodref #9.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #8.#21 // com/zhz/jvm/ClassViewer.c:I
#3 = Class #22 // java/lang/StringBuilder
#4 = Methodref #3.#20 // java/lang/StringBuilder."<init>":()V
#5 = Methodref #3.#23 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#6 = Methodref #3.#24 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#7 = Methodref #3.#25 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#8 = Class #26 // thisClass: com/?/ClassViewer
#9 = Class #27 // superClass: java/lang/Object
#10 = Utf8 c
#11 = Utf8 I
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 add
#17 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 ClassViewer.java
#20 = NameAndType #12:#13 // "<init>":()V
#21 = NameAndType #10:#11 // c:I
#22 = Utf8 java/lang/StringBuilder
#23 = NameAndType #28:#29 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#24 = NameAndType #28:#30 // append:(I)Ljava/lang/StringBuilder;
#25 = NameAndType #31:#32 // toString:()Ljava/lang/String;
#26 = Utf8 com/zhz/jvm/ClassViewer
#27 = Utf8 java/lang/Object
#28 = Utf8 append
#29 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#30 = Utf8 (I)Ljava/lang/StringBuilder;
#31 = Utf8 toString
#32 = Utf8 ()Ljava/lang/String;
{
public com.?.ClassViewer();
descriptor: ()V //构造方法
flags: ACC_PUBLIC //公共
Code:
stack=1, locals=1, args_size=1 // 默认无参构造:使用1个栈Object.init,1个本地变量(全局变量c),1个参数(也是c)
0: aload_0 // 把局部变量表0位元素,也就是c 加载到操作数栈栈顶
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 8: 0
public java.lang.String add(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=3, locals=4, args_size=2 // 本方法调用了3个栈帧,4个局部变量(a,i,j,c),2个参数(a和c)
0: iconst_0 // 常数0压入操作数栈
1: istore_2 // 将栈顶元素 保存到局部变量表2的位置
2: bipush 10 //取值 10 压入栈顶
4: istore_3 // 栈顶元素 保存到局部变量表index=3的位置
5: iinc 2, 1 // 取局部变量表index=2的元素,做+1操作
8: aload_0 //加载局部变量表index=0的值 到栈顶
9: dup
10: getfield #2 // Field c:I 获取#2的全局变量值 =0 压入栈顶
13: iload_3 //加载局部变量表index=3的值到栈顶
14: iadd //取栈顶两个元素做++操作
15: putfield #2 // Field c:I 赋值
18: new #3 // class java/lang/StringBuilder
21: dup
22: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
25: aload_1
26: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: iload_2
30: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
33: iload_3
34: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
37: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
40: areturn
LineNumberTable:
line 11: 0
line 12: 5
line 13: 8
line 14: 18
}
SourceFile: "ClassViewer.java"
JVM指令
load 将局部变量表指定位置的数据 压入 操作数栈 栈顶
- store 将操作数栈 栈顶元素 保存到局部变量表指定位置
- iconst/bipush/sipush/ldc 取值指令,根据取值大小用不同指令,?/byte/short/?/int用ldc,
- iinc 自增
- invokevisual/invokestatic/invokespecial 调用实例方法/调用静态类方法/调用特殊如构造方法
- ireturn 返回
Java ASM框架
ASM API基于访问者模式,为我们提供了ClassVisitor,MethodVisitor,FieldVisitor API接口,每当ASM扫描到类字段是会回调visitField方法,扫描到类方法是会回调MethodVisitor.
//todo
aop的静态织入,还有spring 使用真实参数名(正常class编译是arg0,arg1),也是asm技术。