写点什么

一段 java 代码执行旅程

发布于: 2021 年 03 月 25 日

​​摘要:当你学会了 java 语言之后,你写了一些代码,然后你想要执行你的代码,来达成某些功能。那么,你都知道这段 java 代码都是如何执行的吗?


本文分享自华为云社区《一段 java 代码是如何执行的》,原文作者:李鹏程 。

 

当你学会了 java 语言之后,你写了一些代码,然后你想要执行你的代码,来达成某些功能。那么,你都知道这段代码都是如何执行的吗?

1. 编译成 class


众所周知,java 代码是不能直接在 jvm 上执行的,执行的是 class 文件,将 java 代码编程 class 文件,需要编译。

常用的编译方法是:javac xxx.java

但目前常见的 java 编辑工具,如 eclipse 和 ideal 都自带自动编译动能。

2. jvm 的构成


让我们回忆一下 jvm 的构成:



​主题上分为五个部分:

方法区,本地方法栈,java 堆,java 栈,程序计数器。

其中,java 栈,本地方法栈,程序计数器为线程私有,其余为线程共享。

那么,方法在哪个地方执行呢?

java 栈。

栈的遵循的方式是先进后出,java 栈中方法的执行也遵循此规律,方法执行的步骤又称为栈帧。

3. 方法的顺序执行和栈帧


上代码:


public class Main {public static void a(){b();}
public static void b(){c();}
public static void c(){System.out.println("Hello world!");}
public static void main(String[] args) {a();}}
复制代码


上面是一段很简单的代码,主体上就是:

(1)一个 Main 类

(2)上面定义了一个 main 方法

(3)该 main 方法调用了静态方法 a

(4)方法 a 调用方法 b

(5)方法 b 调用方法 c

(6)方法 c 打印了“Hello world!”


前文说过,java 定义的非本地方法都是在 java 栈内执行的,一方法一栈帧。

所以假设 mian 方法对应栈帧 m,a 方法对应栈帧 a,b 方法对应栈帧 b,c 方法对应栈帧 c,根据方法的调用,入栈顺序为:m,a,b,c,所以,栈帧出栈(即方法执行)顺序为:c,b,a,m。

4. class 文件反编译过后的样子


上一节,方法或栈帧在 java 栈的执行顺序,但在方法体内的内容是怎么执行的呢。

前文提到,jvm 执行的是 class 文件,而 class 文件内是什么?

class 文件内是一组指令集。

如何证明呢,还是再看一段代码。


public class Calculator{public int add(){int n = 10;int m = 20;int r = n + m;return r;}public static void main(String[] args) {Calculator calculator = new Calculator();int a = calculator.add();System.out.println(a);}}
复制代码


如上代码,实现的功能是:

(1)定义两个变量,相加

(2)main 方法 new 对象,调用方法

但,class 文件是不可以直接查看的。我们可以采用反编译的方法,反编译命令:javap -c xxx.class



​上述文件反编译后的样子如下:



每个方法下面的 Code,都是一组指令集。

5. 指令集详解


在讨论指令集之前,首先要讲一个概念,那就是对栈帧进一步拆分。

栈帧一共分为四个部分:局部变量表、操作数栈、动态链接、方法返回地址。

其中,局部变量表和操作数栈是最重要的两个部分。

局部变量表存放在方法中定义的局部变量。

操作数栈相当于 jvm 的一个缓存。

所有的操作都必须在此处进行。

所有的变量都必须加载到操作数栈才能被使用。

所以,所谓指令,就是在局部变量表和操作数栈来回倒腾的过程。


下面对指令进行分类讲解:


(1)入栈指令


整型入栈指令:

取值-1~5 采用 iconst 指令;

取值-128~127 采用 bipush 指令;

取值-32768~32767 采用 sipush 指令;

取值-2147483648~2147483647 采用 ldc 指令。


非整型入栈指令:

float,String 类型也使用 ldc 指令

double 和 long 类型使用 ldc_2w

boolean 类型视作 0 和 1

null 的入栈指令为:aconst_null


(2)存储指令


将操作数栈中的常量保存在局部变量表中的某个位置

如:

istore_1:将上面入栈的整型常量保存在局部变量表中的第 1 个位置

fstore_2:将上面入栈的浮点常量保存在局部变量表中的第 2 个位置

dstore_10:将上面入栈的双浮点常量保存在局部变量表中的第 10 个位置

lstore_20:将上面入栈的长整常量保存在局部变量表中的第 20 个位置

astore_100:将上面入栈的引用常量保存在局部变量表中的第 100 个位置


(3)变量入栈指令


iload_1:局部变量表中的第 1 个位置的整型变量入栈

fload_2:局部变量表中的第 1 个位置的浮点型变量入栈

dload_10:局部变量表中的第 1 个位置的双浮点型变量入栈

lload_20:局部变量表中的第 1 个位置的长整型变量入栈

aload_100:局部变量表中的第 100 个位置的引用型变量入栈


(4)计算指令


加:iadd、ladd、fadd、dadd

减:isub、lsub、fsub、dsub

乘:imul、lmul、fmul、dmul

除:idiv、ldiv、fdiv、ddiv

注意:栈顶计算,一次只能计算一个表达式


点击关注,第一时间了解华为云新鲜技术~


发布于: 2021 年 03 月 25 日阅读数: 7
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
一段java代码执行旅程