P30系列10.1.0.118版本发布
什么是JVM
这个大家都应该很熟悉了吧,JVM不就是虚拟机吗?
Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。
JVM可以说离我们既熟悉又陌生,很多朋友可能在工作中接触不到这块技术,但是在面试往往被问到(概率还蛮大),被问到了自认倒霉,死记硬背是没用的,到头来还是的忘,今天给大家说道说道JVM知识点,我要没让你明白算我输,你可以留言喷我,如果要是可以,你们也给我点个赞成不?
初识JVM
相信这张图大家都不陌生,这是整个Java体系,其中包括JDK.JRE.JVM三者的关系。
图中可以看得出来JRE包含了JVM,JDq ~ s 9 O pK包含了JRE。
从包含的角度就是: JDK是爷i Y ) & s R B M r爷 JRE是父亲 JVM是儿h * k l子(如果觉得列子不太恰当)来看图! q N 2 l O $ ?
我们来看代码:
pub8 Y e t b = 2;
int c = (a + b) * 100;
return c;
}
/**
* 程序入口
*/
pub4 x $ = , h Flic static void main(String args) throws InterrupteM l * L _dException {
A= J 3 Mpp app = new App();
int result = app.add();
System.out.println(result);
}
}
我们运行上A ] n s 3 %述代码输出结果是300,虽然这个代码非常简单,这个时候已经涉及到JVM相关的知识了,在我们c C =学Java基础的时候老师就告诉我们,Java是跨平台的,一次编写到处运行。
那Java是怎么做到跨平台的?继续看下图:
通过此图大家就不难发现,我们编译的App.class文件可以在Windows操作系统% e H C 6 ) + 5 2运行也可以在Linux系统运行,但是两个系统底层的操作指令是不一样的,为了屏蔽底层指令的细节,起到一个跨平台= 6 w Q n的作用,Ju 8 K ?VM功不可没,我们常说Java是跨平台还不如说是Jvm跨平台(JRE运行时跨平台)。那Jvm虚拟机是怎么跨平台的Z $ 5 k % ^ z = )?
JVM底层原理
JVM底层由三个系统构成分别2 4 n f是:类加载、运行时数据区、执行引擎。
我们今天重点讲解JVM运行时数据区(栈),其他两块可以关注我之前和后续文章。
我们App.clasO ) /s文件通过类加载子系统从硬盘中读取文件加载到内存中(运行时数据区)。
加载完成之后怎么处理了?b Z Z(打个比喻 人吃饭 》吃到肚子里》各各@ i b 0器官负责自己工作吸收)
Stack栈
先讲一下j s Y其中的一块内存区域虚拟机栈,大家都知道栈是数据结构,也是线程独有的区域,也就是每一个线程都会有自己独立的栈区域。我们运行App.java输出300就靠线程执行得来的结果。是哪个线程执行的?获取线程快照:“main线程”
栈》数据结构》存储内容》先进后出FILO
大家都知道每个方法都有自t R j ? G己的局部变量,比如上图中maS t Q & [ 4那么栈又会分配add()的栈帧区域。
这里的栈存储数据的方b l H "<init>r n : A y N = [\":()VL 3 k q x S =
4: aload_0
5: neO J 9 . f H C |w #2 // class java/lang/Object
8: dup
9: invokespecial #1 // Method java/lang/Object.\"&/ | e B p 9lt;init>\":()V
12: putfield #3 // Field object:Ljava/lang/Object;
15: return
public int add();
Code:
0: iconst_1
1: istorr ; _ K v 6 &e_1
2: iconst_2
3: istore_2
4: ilj G U 1 S D = / Ooad_1
5: iload_2
6: iadd
7: bipush 100
9: imul
10: istore_3
11: iload_3
12: ireturn
public static void main(java.lang.String) throws java.lang.InterN 2 2 oruptr z r 5 l i 8edException;
Code:
0: new #4 // class com/App
3: dup
4: invokespecial #5 // Method \"<init>\":()V
7: astore_1
8: alo, _ X T 0 Vad_1
9: invokevirtual3 E 0 ; #6 /+ } 5 { : b I V // Method add:()I
12: istore_2
13: getstatic #7 // Field java/lang/System.out:Ljava/io/Pr2 l IintStream;
16: iload_2
17: invo; H M okevirtual #8 // Method java/ioE l f F a m i n/PrintSt_ t o C P / h $rG , [ ? X I M # ,eam.printu H y . - g gln:(I)V
20: return
}
此时的jvm指令码就清晰很多了,大体结构是可以看懂的,类、静态变量、构造z = J _ 4 s W G j方法、add()方法、main()方法。
其中方法中的指令还是有点懵,我们举add()方法来看一下:
Cob T p .de:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iA 0 P c D s @add
7: bipush 100
9: imul
10:f O O [ E A istore_3
11: iload_3
12: iretu
这几行代码就是对应的我们代码中add()方法中的四行代码。大家都知道越底层的代码,代码实现的行数越多,因为他会包含一些java代码在运行时底层隐藏的一些细节原理。
那么一样的,这个jvm指令官方也是有手册可以查阅的,网上也有很多翻译版本,大a @ n ( r O 3 ;家如果想了解可自行百度。
执行流程
设计代 z Z y . A 码中的部分指令含义:
第一步:压栈
将int类型常量1压入操作数栈
0: iconst_1
就是将1压入操作数栈
更正:执行流程过程中灰色背景“操作数栈a L u”应改为“局部变量表”(!!M , K v (!!!!!!!!)。
第二步6 c ):存储
将int类型值存入局部变量1
1: istore_1
局部变量1,在我们代码中也就是第一个局部变量a,先给a在局部变量表中分配内存,然后将int类型的值,也就是目前唯一的一个1存入局部变量a
第三步:赋值
这两行代码就和前两行类似了。
2: iconst_2
3: istore_2
第四步:X * w q y装载 t L j &
从局部变量2中装载int类型值
4: iload_1
5: iload_f D ) l
这里就类似上面的加法了,将3和100弹出栈,把结果300压入栈
第八步:压栈
将将int类型值存入% ) a Z G + 3 K局部变量3
10: isto9 x u ) 8 Are_3
这里大家就不陌生了吧,和第二步第三步是一样的,将300存入局部变u ] 2 T n量3,也就是c
第九步:装载
从局部变量3中装载int类型值
11: iload_3
从局表变量3加载到操作数栈
第十步:返回
返回int类型值
12: ireturn
我们add方法是被main方法中调用的,所以通过方法出口返回p q ^ Y : j l到mian方法中result变量存储方法出口说白了不就是方法c A 5 | U P - C @执行完了之后要出到哪里,那么我们知道上面add()方法执行完之后应该回到main()方法第三行那么E ? . c r t *当main()方法调用adu @ ) l 2 ;d()的时候,add()栈- Y 0 ~ A 9 r . K帧中的方法出口就存储了当前要回到的位置,那么当add()方法执行完之后,会根据方法出口P t D R z r 6中存储的相关信息回到ma& t 4 l 7in()方法的相应位置。看我图中的红线
栈堆关系
main方法中除了result变量还有一个app变量,app变量指向的是一个对X ; h H E象。那对象是怎么存储的?这儿要在说下局表变量表结构:基本类型 Q L和引用类型(Java叫引用C C++叫指针)
关系就T l i / 8 l { ,是:
通过引用在栈中的am H n 8 F J h 2pp变量引用c ; W p ] : d 6堆中的App对象
总结D g ! 9 O * X
讲到这儿相信大家对JVM栈执行原理是不是熟悉了?如果觉得不错欢迎点赞评论。