持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天
1+2在JVM中的处理过程是什么样的?
接上几篇文章,我们了解了JVM运行时数据区域模型,JVM中的常用操作指令编码。下面我们来通过简单的例子a+b看看整个处理过程是怎么样的。
资料:
1、源码
public class AddDemo {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = add(a, b);
System.out.println("c=" + c);
}
/***
* a+b
* @param a
* @param b
* @return
*/
public static int add(int a, int b) {
return a + b;
}
}
2、Class文件
因为JVM能够读懂的是Java源码编译后的Class文件,所以我们要看Class文件。源码反编译后的文件如下:
Classfile /Users/zhangjian/works/workspace_home/docker-demo/target/classes/com/jianjang/docker/study/base/AddDemo.class
Last modified 2022-10-22; size 987 bytes
MD5 checksum 667fc2791a7668878bb530f04b32b4f2
Compiled from "AddDemo.java"
public class com.jianjang.docker.study.base.AddDemo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #12.#33 // java/lang/Object."<init>":()V
#2 = Methodref #11.#34 // com/jianjang/docker/study/base/AddDemo.add:(II)I
#3 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Class #37 // java/lang/StringBuilder
#5 = Methodref #4.#33 // java/lang/StringBuilder."<init>":()V
#6 = String #38 // c=
#7 = Methodref #4.#39 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #4.#40 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#9 = Methodref #4.#41 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#10 = Methodref #42.#43 // java/io/PrintStream.println:(Ljava/lang/String;)V
#11 = Class #44 // com/jianjang/docker/study/base/AddDemo
#12 = Class #45 // java/lang/Object
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this
#19 = Utf8 Lcom/jianjang/docker/study/base/AddDemo;
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 a
#25 = Utf8 I
#26 = Utf8 b
#27 = Utf8 c
#28 = Utf8 MethodParameters
#29 = Utf8 add
#30 = Utf8 (II)I
#31 = Utf8 SourceFile
#32 = Utf8 AddDemo.java
#33 = NameAndType #13:#14 // "<init>":()V
#34 = NameAndType #29:#30 // add:(II)I
#35 = Class #46 // java/lang/System
#36 = NameAndType #47:#48 // out:Ljava/io/PrintStream;
#37 = Utf8 java/lang/StringBuilder
#38 = Utf8 c=
#39 = NameAndType #49:#50 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#40 = NameAndType #49:#51 // append:(I)Ljava/lang/StringBuilder;
#41 = NameAndType #52:#53 // toString:()Ljava/lang/String;
#42 = Class #54 // java/io/PrintStream
#43 = NameAndType #55:#56 // println:(Ljava/lang/String;)V
#44 = Utf8 com/jianjang/docker/study/base/AddDemo
#45 = Utf8 java/lang/Object
#46 = Utf8 java/lang/System
#47 = Utf8 out
#48 = Utf8 Ljava/io/PrintStream;
#49 = Utf8 append
#50 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#51 = Utf8 (I)Ljava/lang/StringBuilder;
#52 = Utf8 toString
#53 = Utf8 ()Ljava/lang/String;
#54 = Utf8 java/io/PrintStream
#55 = Utf8 println
#56 = Utf8 (Ljava/lang/String;)V
{
public com.jianjang.docker.study.base.AddDemo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/jianjang/docker/study/base/AddDemo;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=1
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: invokestatic #2 // Method add:(II)I
9: istore_3
10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
13: new #4 // class java/lang/StringBuilder
16: dup
17: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
20: ldc #6 // String c=
22: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: iload_3
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: return
LineNumberTable:
line 13: 0
line 14: 2
line 15: 4
line 16: 10
line 17: 35
LocalVariableTable:
Start Length Slot Name Signature
0 36 0 args [Ljava/lang/String;
2 34 1 a I
4 32 2 b I
10 26 3 c I
MethodParameters:
Name Flags
args
public static int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: iload_0
1: iload_1
2: iadd
3: ireturn
LineNumberTable:
line 26: 0
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
MethodParameters:
Name Flags
a
b
}
SourceFile: "AddDemo.java"
3、执行过程解读
关键字节码如下
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=1
0: iconst_1 //将常量1加载到操作数栈
1: istore_1 //将1从操作数栈存储到局部变量表
2: iconst_2 //将常量2加载到操作数栈
3: istore_2 //将2从操作数栈存储到局部变量表
4: iload_1 //将局部变量表的常量1压入操作数栈
5: iload_2 //将局部变量表的常量2压入操作数栈
6: invokestatic #2 // Method add:(II)I //执行add方法
9: istore_3 //将3从操作栈存储到局部变量表
//add方法
public static int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: iload_0 //将局部变量表的第1个常量压入操作数栈
1: iload_1 //将局部变量表的第2个常量压入操作数栈
2: iadd // 将最近的两个常量相加,并将结果压入操作数栈
3: ireturn //将结果压入调用方的栈顶(main方法栈顶)
示意图1-main栈帧
示意图2-add栈帧
至此,整个操作过程解析结束。个人理解,有问题的话,感谢联系改正。谢谢。