导语
假如面试中问到Java程序到底是如何执行的你该怎么回答?也许大多数人的脑海中会有这样一个画面:
如果说你面试的是一个初级岗位的话能把这幅图描述出来就可以了,但这不是我们的目的。作为一个开发者更好的了解其原理的话对你的程序开发肯定会有一定的提升,那到底应该怎么回答才能让面试者感觉到确实了解原理呢?带着这个问题我们接着往下看。
准备工作
首先我们准备这样一段代码
然后执行如下两条命令
然后得到这样的几个文件
刚才上面输出的test.txt是我们今天讲的重点(直接导入你的编辑器看会比较舒服)
执行过程
在看执行过程之前,按照上面的代码我画了一张图来帮助大家理解
这是main方法在执行过程中的一个大概的流程:
首先每一个线程都有他自己的程序计数器、栈、本地方法栈
然后线程中的每一个方法都代表一个栈帧,这里就是main栈帧和math栈帧,其中math栈帧表示正在执行的栈帧
其次每个栈帧在执行过程中都有他自己的局部变量表、操作数栈、动态链接、方法出
过程分析
看到这个txt的第一眼先不要惊慌,慢慢一行一行看你就明白了。这里我对每一个jvm指令都加上了注释方便大家看,如果想了解更多的话可以去网上搜一下。仔细一看的话他这里其实就是一个堆栈操作的过程,也是一个难点,跟我之前分享的“纠结已久的项目终于落地了”里面的逆波兰表达式有几分相似。下面我们就来看看这里的堆栈操作到底是怎么执行的:
以math方法中的执行为例
iconst_1:将int类型的值1推送至栈顶,然后进行压栈;当前程序计数器指向0
istore_1:将栈顶int类型的数值存入第一个本地变量;当前程序计数器指向1
iconst_2:将int类型的值2推送至栈顶,然后进行压栈;当前程序计数器指向2
istore_2:将栈顶int类型的数值存入第二个本地变量;当前程序计数器指向3
这两步其他就是在重复前两步,执行完之后大概是这样:
iload_1:将第1个int类型的本地变量推送至栈顶,然后进行压栈;当前程序计数器指向4
iload_2:将第2个int类型的本地变量推送至栈顶,然后进行压栈;当前程序计数器指向5
iadd:将栈顶两个int类型数值相加并将结果压入栈顶,然后进行压栈;当前程序计数器指向6
bipush:将单字节的变量值推送至栈顶;当前程序计数器指向7
imul:将栈顶两int类型数值相乘并将结果压入栈顶;当前程序计数器指向9,这里直接就到9了,因为10这个变量也占用一个内存地址也就是这里没体现出来的8
istore_3:将栈顶int型数值存入第四个本地变量;当前程序计数器指向10
iload_3:将第3个int型本地变量推送至栈顶,然后进行压栈;当前程序计数器指向11
ireturn:从当前方法返回int;当前程序计数器指向12
这里就直接就30返回了,当然这里的返回是通过当前栈中的方法出口中的指针来记录返回的。方法的推出由两种方式:正常退出或者是异常退出,我们这里是属于前者。
动态链接
假如说我们的代码中定义了一个map,map是存在我们当前的栈的局部变量表中的,但是map中的元素是是放在堆中的,当我们需要引用map中元素的时候这个过程就相当于是动态链接。
总结
今天分享的主要是一些原理方面的知识,实际工作中是不会用到的,但是如果面试过程中你能说出来的话绝对会让面试官对你刮目相看的。可以不去造火箭,但是造的这个原理你得懂,等你真正从大厂面试出来之后你就明白了。
篇幅有点长,看到这里的小伙伴帮忙点赞转发一下吧,谢谢!