Java程序代码是如何执行的? 值得一看(Java程序执行)
当你学习了java语言之后,你写了一些代码,然后你想要执行你的代码来实现某些功能。 那么,大家知道这段Java代码是如何执行的吗?
1.编译成类
众所周知,java代码不能直接在jvm上执行。 它是一个被执行的类文件。 要将java代码编程到类文件中,需要对其进行编译。
常用的编译方法是:javac xxx.java
但目前常见的java编辑工具,比如and Ideal,都有自己的自动编译动能
2.jvm的组成
我们先回顾一下jvm的组成:
按主题分为五个部分:
方法区、本机方法栈、java堆、java栈、程序计数器
其中java栈、本地方法栈、程序计数器为线程私有,其余为线程共享
那么,该方法在哪里执行呢?
java 堆栈。
栈遵循的方式是先进后出,java栈中方法的执行也遵循这个规则。 方法执行的步骤也称为堆栈帧。
3.方法和栈帧的顺序执行
上面的代码:
java代码
类主要{
无效a(){
b();
}
无效 b(){
C();
}
无效 c(){
.out.("你好世界!");
}
无效主([]参数){
A();
}
}
上面是一段很简单的代码,主体是:
(1)主类
(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文件里有什么内容呢?
类文件内部是一组指令。
如何证明呢,我们再看一段代码。
java代码
班级 {
int 添加(){
整数 n = 10;
整数米 = 20;
int r = n + m;
r;
}
无效主([]参数){
=新的();
int a = .add();
.out.(a);
}
}
如上面的代码,实现的功能是:
(1)定义两个变量并相加
(2)main方法新建对象,调用方法
但是,不能直接查看类文件。
我们可以使用反编译方法来反编译命令:
javap -c xxx.class
上面的反编译文件如下所示:
每个方法下的代码都是一组指令。
5. 详细指令集
在讨论指令集之前,我们首先要讲一个概念,就是进一步拆分栈帧。
栈帧分为四部分:局部变量表、操作数栈、动态链接、方法返回地址
其中,局部变量表和操作数栈是最重要的两个部分。 局部变量表存储了方法中定义的局部变量。 操作数栈相当于jvm的一个缓存。 所有操作都必须在这里进行。 所有变量必须先加载到操作数堆栈中才能使用。 所以,所谓指令就是在局部变量表和操作数栈之间来回折腾的过程。
指令分类如下:
(1)推送指令
整数入栈指令:
值-1~5采用命令;
值-128~127采用指令;
值-32768~32767采用该指令;
值 - ~ 使用 ldc 命令。
非整数入栈指令:
float,类型也使用ldc指令
和长型使用
输入 0 和 1
null的push指令为:
(2) 存储说明
将操作数栈中的常量保存在局部变量表的某处
喜欢:
:将上面压入栈的整型常量保存在局部变量表的第一个位置
:将上面压入栈的浮点常量保存在局部变量表的第二个位置
:将上面压入栈的双浮点常量保存在局部变量表的第10个位置
:将上面压入栈的long常量保存在局部变量表的第20位
:将上面压入栈的引用常量保存在局部变量表的第100位
(3) 变量压入指令
:将局部变量表中第一个位置的整型变量压入栈中
:将局部变量表中第一个位置的浮点变量压入堆栈
:将局部变量表中第一个位置的双精度浮点变量压入堆栈
:将局部变量表中第一个位置的长整型变量压入堆栈
:将局部变量表中第100位的引用变量压入堆栈
(4) 计算说明
添加: iadd、ladd、fadd、dadd
减:isub、lsub、fsub、dsub
乘法:imul、lmul、fmul、dmul
除法:idiv、ldiv、fdiv、ddiv
注意:在栈顶计算,一次只能计算一个表达式