Comments (11)
jvm指令是如何执行的?
接下来就是我们的重点内容了,Jvm指令是如何执行的。
jvm规定了一系列的指令来用来决定jvm的流程,
这些指令打给有下面的类型:
- 加载和存储指令,比如 iload 将一个整形值从局部变量表加载到操作数栈
- 控制转移指令,比如条件分支 ifeq
- 对象操作,比如创建类实例的指令 new
- 方法调用,比如 invokevirtual 指令用于调用对象的实例方法
- 运算指令和类型转换,比如加法指令 iadd
- 线程同步,monitorenter 和 monitorexit 两条指令来支持 synchronized 关键字的语义
- 异常处理,比如 athrow 显式抛出异常
接下里我会列举出一线典型的指令来和大家一起学习,但是我不可能说道所有的指令,后续的内容需要大家自行查看jvm规范。
我们先开看看这个简单的方法
public int add(int a, int b){
return a + b;
}
它编译后的指令如下:
public int add(int, int);
descriptor: (II)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: iadd
3: ireturn
LineNumberTable:
line 3: 0
然后我们首先来看一些,stack: 我们的jvm指令调动是基于栈的,这是什么意思呢,就是说我们的
把变量加载到执行栈:进行一些操作,将结果返回或者存储到本地变量。
stack表明我们的栈的最大深度是2,这是编译阶段编译器解析出来的。
locals 是本地变量的数量,他就是 this, a, b。这里注意,对于jvm来说,他不认识什么this,a,b。他只知道本地变量表中的第一个元素,第二个元素,第三个元素。。。。这一点非常重要,我们后续的指令完全是基于所以完成变量的加载的。这一点请务必牢记。
然后我介绍下上面四个指令的含义,jvm的指令非常多,我不可能一一介绍,建议大家在阅读完我的教程后,细读jvm规范。
iload_1 将本地变量表中索引为1的元素加入到操作栈中
- 针对的int类型的,如果float类型的,fload_1, double类型的就是dload_1
同理iload_2 就是将本地变量表中索引为2的元素加入栈中
iadd就是取出栈顶的连个元素,让它们相加,再将结果放入栈中
pop two values from stack,retuqire them are int, and add them, then push result to stack
ireturn将栈顶的元素返回。
pop value from stack and return this value。
然后我们来通过图示看一看 add(10, 20)?
from blogss.
https://www.processon.com/view/link/5d1def06e4b0316252cfa2bf
from blogss.
怎么样,是不是很清楚? 然后我们再来看一个示例
public class Hello {
public int add(int a, int b){
int c = a + b;
return c;
}
}
Code:
stack=2, locals=4, args_size=3
0: iload_1
1: iload_2
2: iadd
3: istore_3
4: iload_3
5: ireturn
istre后栈空了
iload
ireturn
发现了吗?前者后后者的功能一模一样,但是前者多了一个本地变量c以及两个没有意义的指令,所以写代码的时候尽量选择前者。
from blogss.
下面再来看一个有意思的例子。已知字节码指令为
public int action(int, int, int);
descriptor: (III)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=4, args_size=4
0: iload_3
1: iload_1
2: isub
3: iload_2
4: isub
5: ireturn
LineNumberTable:
line 3: 0
}
请问你可以反向推导出对应的java代码吗?
提示
isub 这个指令是:
- pop a value from stack,call value1, require it is int
- pop a value from stack, call value2, require it it int
- push(value2 - value1) to stack
让栈顶的两个int型元素相减
假设三个变量是
首先很明显 ,长这样的
public class Hello {
public int action(int param1, int param2, int param3){
}
}
0: iload_3
1: iload_1
2: isub
c -b
3: iload_2
c -b -a
4: isub
5: ireturn
return
from blogss.
from blogss.
那么你明白结果了吗?
return param3 - param1 -param2
怎么样?是不是很有趣?
from blogss.
if else
接下里我们分析下if else 是如何运行。
这是我们的java代码,很假单,就是返回两个值中比较大的那个。
public class Hello {
public static int maxValue(int p1, int p2){
if (p1 > p2){
return p1;
} else {
return p2;
}
}
}
下面是编译后的字节码
public static int maxValue(int, int);
descriptor: (II)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: iload_0
1: iload_1
2: if_icmple 7
5: iload_0
6: ireturn
7: iload_1
8: ireturn
然后你注意到了吗,这里的本地变量只要两个。应为我们的static方法 没有this。
然后就是if_complet
假设当前的操作是栈是这样的
// here is top of stack
value1
value2
...
// here is buttom of stac
这时候如果我们执行if_icmple 7
,如果value2 < value1 那么指令跳转到7的位置,否者不跳转,安装顺序向下执行。
这是图示:
from blogss.
for循环
if_icmple 是一个跳转指令,还有一个goto
java中goto是不合法的,但是在指令集中有。我们来看一下
public class Hello {
public static void loop(){
for (int i = 0; i < 3; i++) {
}
}
}
Code:
stack=2, locals=1, args_size=0
0: iconst_0
1: istore_0
2: iload_0
3: iconst_3
4: if_icmpge 13
7: iinc 0, 1
10: goto 2
13: return
接受三个新的指令,
iconst_0 将 0压入操作栈
iconst_1 将 1 压入操作栈
iinc 将本地变量表中,索引为0的本地的值+1
goto,跳转到对应的指令位置执行。
from blogss.
from blogss.
上面的代码中,我们执行了一遍for循环。显然,第三次的时候i的值会到3,将会跳出循环。
from blogss.
Switch 原理
如果让你来设计一个 switch-case 的底层实现,你会如何来实现?是一个个 if-else 来判断吗? 实际上编译器将使用 tableswitch 和 lookupswitch 两个指令来生成 switch 语句的编译代码。为什么会有两个呢?这充分体现了效率上的考量。
int chooseNear(int i) {
switch (i) {
case 100: return 0;
case 101: return 1;
case 104: return 4;
default: return -1;
}
}
字节码如下:
0: iload_1
1: tableswitch { // 100 to 104
100: 36
101: 38
102: 42
103: 42
104: 40
default: 42
}
36: iconst_0 // return 0
37: ireturn
38: iconst_1 // return 1
39: ireturn
40: iconst_4 // return 4
41: ireturn
42: iconst_m1 // return -1
43: ireturn
细心的同学会发现,代码中的 case 中并没有出现 102、103,为什么字节码中出现了呢? 编译器会对 case 的值做分析,如果 case 的值比较紧凑,中间有少量断层或者没有断层,会采用 tableswitch 来实现 switch-case,有断层的会生成一些虚假的 case 帮忙补齐连续,这样可以实现 O(1) 时间复杂度的查找:因为 case 已经被补齐为连续的,通过游标就可以一次找到。
伪代码如下
int val = pop(); // pop an int from the stack
if (val < low || val > high) { // if its less than <low> or greater than <high>,
pc += default; // branch to default
} else { // otherwise
pc += table[val - low]; // branch to entry in table
}
我们来看一个 case 值断层严重的例子
int chooseFar(int i) {
switch (i) {
case 1: return 1;
case 10: return 10;
case 100: return 100;
default: return -1;
}
}
对应字节码
0: iload_1
1: lookupswitch { // 3
1: 36
10: 38
100: 41
default: 44
}
如果还是采用上面那种 tableswitch 补齐的方式,就会生成上百个假 case,class 文件也爆炸式增长,这种做法显然不合理。lookupswitch应运而生,它的键值都是经过排序的,在查找上可以采用二分查找的方式,时间复杂度为 O(log n)
结论是:switch-case 语句 在 case 比较稀疏的情况下,编译器会使用 lookupswitch 指令来实现,反之,编译器会使用 tableswitch 来实现
from blogss.
Related Issues (6)
- Class File Format HOT 7
- 类相关指令 HOT 8
- 通过字节码来理解Java
- 破解jvm软件 HOT 4
- 链表相关的算法
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blogss.