Java的底层其实就是JVM(Java虚拟机);对于这个底层的学习一直是比较困难的,看书和看视频呢,讲得都是非常底层的一些理论,甚至涉及到计算机组成原理内容更多;实话说上大学时候老师是根本不讲JVM的,简单粗暴讲Java怎么用,有什么特性。

所以在工作后,为了更进一步的发展,对自己所用到的技术也要有更深一步的理解,这才慢慢接触和学习。虽然平时业务开发肯定是用不上的(除非有大神做的底层开发优化之类的),但是清晰了解总比迷糊不清好很多。

栈:

栈分两种:本地方法栈 和 虚拟机栈。两者没有本质上的区别,区别在于服务的对象不同。
本地方法栈服务于JVM虚拟机的native方法。虚拟机栈服务于虚拟机执行的Java方法。JDK 安装目录下就会有很多C的文件,这些就都是native方法。
栈和线程是一一对应的,一个栈对应一个线程,多线程进行切换的时候,也是在进行栈的切换。

(1)native方法就是Java方法去调用非Java的方法的接口,例如Java底层的C语言的方法就是一个很好的例子,被native标识符标志的话就表明该方法是有实现方法体的,也可以通过其它修饰符来表明这个方法的具体状态,例如native static同时用,就表明可以直接调用无需创建实例,然后它的返回类型也是支持Java的全部类型。
(2)为什么要用Java去调其他语言的方法呢?
因为有的方法实现起来用Java不方便(例如和计算机硬件的交互,单片机那些,操作系统那些),用Java让我自己写,我还真是想像不到面向对象语言怎么去很好地实现这操作,用其他语言例如C等可以很方便,运用不同的语言特性可以达到最优效果。而Java语言也不是写那些底层的,它说高级语言其实更偏向于解决生活中的业务需求。所以这种封装也让Java编程者不必要去了解到Java底层与硬件的具体交互实现。

栈里面有:
栈帧:

栈帧其实就是栈里面的元素。多线程每个栈都是相互独立互不影响的;同时各个栈帧之间也是独立的,调用一个方法就是入栈一个栈帧,方法的参数和一些变量肯定也是独立的,只有小部分重复的会与其它栈帧共享。

打个比喻:
一条曼妥思薄荷糖就是一个栈,栈帧就是一片片糖果,栈特性就是先入后出,后入先出,那也是因为你打开一条曼妥思糖果肯定是只打开一端,先放进去的糖果肯定是最后一颗,最后一颗填充进去的肯定是被吃的第一颗。

那么,大家都说栈是存储 局部变量表、操作数栈、动态链接、方法出口等信息的。其实说的就是栈帧。

比喻:你说曼妥思很好吃,但是不可能是连带着包装纸一起吃,肯定是说里面的糖果片(栈帧)好吃。所以存储那些爆浆,糖分,不同口味的内容的就是具体的糖果片栈帧。

小概念知识点:
什么是动态链接?动态链接怎么实现?

动态链表就是当前方法要用到其它方法的参数或者调用到其它方法的时候,就要根据方法名去找到对应方法对应的栈帧的内存地址。
每一个栈帧内部都包含一个指向运行时常量池的引用,来支持动态链接的实现。
比喻:听说吃了曼妥思再喝可乐会很好吃,于是你在吃曼妥思的时候根据“可乐”这种饮料名字去冰箱找到了可乐的地址。也就是你在吃曼妥思这一动作动态链接到了喝可乐这一动作。

追根溯源:什么是运行时常量池?

运行时常量池是JVM虚拟机在运行加载类的时候(也就是将Java文件编译成.class字节码文件的时候),把.class文件里面的class常量池放到运行时常量池中,运行时每个类加载一次,产生一个字节码.class文件,所以运行时常量池也是一个类对应一个。

所以说栈帧才是具体是用来存储数据,过程结果,处理动态链接 ,方法返回值,异常分派 等任务的。
一个栈帧对应一个方法,栈最顶层的栈帧就是正在执行的方法,所以也叫“当前栈帧”,一个个方法的入栈,就是被调用,出栈了就是该方法执行完了,所以携带着返回值。所以代码中发生error的时候,打印堆栈信息打出来的 e.stack;就能定位到发生错误的具体哪个方法。

比喻:曼妥思被放进糖果包装纸条里就是准备好了被吃,每一颗都带着自己独特的口味和爆浆口感,出栈了就是代表被拿出去吃了。在栈顶就表示正在等待被吃。

栈的大小什么时候确定:
编译时就根据变量类型来确定分配内存大小,不受运行时影响改变大小。

比喻:工厂制作的时候就已经根据不同幸好和口味的曼妥思糖果分配了不一样的包装纸,你买来吃的时候影响不了它的包装。

操作数栈:

JVM7 里面关于操作数栈(又称操作栈)的解释:

Each frame (§2.6) contains a last-in-first-out (LIFO) stack known as its operand stack. Where it is clear by context, we will sometimes refer to the operand stack of the current frame as simply the operand stack.The operand stack is empty when the frame that contains it is created. The Java virtual machine supplies instructions to load constants or values from local variables or fields onto the operand stack. Other Java virtual machine instructions take operands from the operand stack, operate on them, and push the result back onto the operand stack. The operand stack is also used to prepare parameters to be passed to methods and to receive method results.
百度翻译:
每帧(§2.6)包含一个后进先出(LIFO)堆栈,称为其操作数堆栈。在上下文清楚的地方,我们有时会将当前帧的操作数堆栈简单地称为操作数堆栈。当包含它的帧被创建时,操作数堆栈是空的。Java虚拟机提供将常量或值从局部变量或字段加载到操作数堆栈的指令。其他Java虚拟机指令从操作数堆栈中获取操作数,对其进行操作,然后将结果推回到操作数堆栈中。操作数堆栈还用于准备要传递给方法的参数和接收方法结果。
个人翻译:
栈帧刚被创建时操作数栈是空的,Java虚拟机提供指令来让操作数栈对一些数据进行入栈操作,比如将常量,变量,实例对象的字段加载到栈帧的方法中。
栈帧中的方法传参,入参,出参,都存在操作数栈中。

总结:

操作数栈随着栈帧中方法的执行和字节码文件(.class文件)指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。一个完整的方法执行期间往往包含多个这样出栈/入栈的过程,这里反复的出入栈,就是操作数栈在不断地,多次地 执行出栈入栈行为。
其实就是很普通的一个附值语句:int a = 1 ,字节码命令也已经让操作数栈执行了多次出入栈操作。

这里有一篇很简单易懂的解析Java解析为字节码后的出栈入栈执行的流程:
https://blog.csdn.net/weixin_43217817/article/details/101381363

个人总结就先到这里,如果有说错的地方或者理解错的地方欢迎留言给我修正。鞠躬!
Java中的栈(JVM底层详解,看完不懂找我拿糖)

相关文章:

  • 2022-12-23
  • 2022-01-19
  • 2021-08-07
  • 2021-12-28
  • 2021-08-22
  • 2021-04-13
  • 2021-07-01
  • 2021-05-01
猜你喜欢
  • 2021-09-03
  • 2022-01-08
  • 2021-04-07
  • 2021-07-24
  • 2021-08-02
  • 2021-11-22
  • 2021-11-08
相关资源
相似解决方案