对于 JVM(Java 虚拟机)来说,它有两个非常重要的区域,一个是栈(Java 虚拟机栈),另一个是堆。堆是 JVM 的存储单位,所有的对象和数组都是存储在此区域的;而栈是 JVM 的运行单位,它主管 Java 程序运行的。那么为什么它有这样的魔力?它存储的又是什么数据?接下来,我们一起来看。

1.栈定义

我们先来看栈的定义,我们这里的栈指的是 Java 虚拟机栈(Java Virtual Machine Stack)也叫做 JVM 栈,《Java虚拟机规范》对此区域的说明如下:

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous.

In the First Edition of The Java Virtual Machine Specification, the Java Virtual Machine stack was known as the Java stack.

This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created.

A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes.

The following exceptional conditions are associated with Java Virtual Machine stacks:

  • If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError.
  • If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an OutOfMemoryError.

以上内容翻译成中文的含义如下:

Java 虚拟机栈是线程私有的区域,它随着线程的创建而创建。它里面保存的是局部变量表(基础数据类型和对象引用地址)和计算过程中的中间结果。Java 虚拟机的内存不需要连续,它只有两个操作:入栈和出栈。

Java 虚拟机栈要么大小固定,要么根据计算动态的扩展和收缩。程序员可以对 Java 虚拟机栈进行初始值的大小设置和最大值的设置。

Java 虚拟机栈出现的异常有两种:

  • 当 Java 虚拟机栈大小固定时,如果程序中的栈分配超过了最大虚拟机栈就会出现 StackOverflowError 异常。

  • 如果 Java 虚拟机栈是动态扩展的,那么当内存不足时,就会引发 OutOfMemoryError 的异常。

    2.栈结构

    栈是线程私有的,每个线程都有自己的栈(空间),栈中的数据是以栈帧(Stack Frame)的形式存在的,线程会为每个正在执行的方法生成一个栈帧,如下图所示:

    PS:当一个新的方法被调用时,就会在栈中创建一个栈帧,当方法调用完成之后,也就意味着这个栈帧会执行出栈操作。

而栈帧中又存储了 5 个内容:

  1. 局部变量表(Local Variables);
  2. 操作(数)栈(Operand Stack);
  3. 动态链接(Dynamic Linking);
  4. 方法返回地址(Return Address);
  5. 附加信息。

如下图所示:



栈的整体存储结构如下图所示:

2.1 局部变量表

局部变量表也叫做局部变量数组或本地变量表。

局部变量表是一个数组,里面存储的内容有:

  • 方法参数;
  • 方法内的局部变量,也就是方法内的基本数据类型和对象引用(Reference);
  • 方法返回类型(Return Address)。

接下来我们通过类生成的字节码来观察一下局部变量表的内容,首先,我们先来搞一个 main 方法,具体代码如下:

public static void main(String[] args) {
int num = 0;
LocalVariablesExample lv =
new LocalVariablesExample();
}

然后我们编译类,再使用“javap -v LocalVariablesExample.class”查看字节码生成的内容,其中包含的本地变量表内容如下:



我们通过 JClassLib 也能观察到局部变量表的信息,如下图所示为局部变量表的长度:



局部变量表的详细信息如下:

2.2 操作栈

操作栈也叫做操作数栈或表示式栈,操作数栈主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。

思考:为什么不把程序执行过程中的中间结果保存到局部变量表,而是保存到操作数栈中呢?

因为局部变量表是数组,而数组的长度是在其创建时就要确定,所以局部变量表在编译器就决定内容和大小了,那么在程序执行中的这些动态中间结果,是需要新的空间来保存了,而操作数栈就可以实现此功能。

2.3 动态链接

动态链接也叫做指向运行时常量池的方法引用。

这个区域的概念和作用稍微难理解一点,在每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。当一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

也就是说:当一个方法调用另一个方法时,不会再创建一个被调用的方法,而是通过常量池的方法引用来调用,而这个区域存储的就是运行时常量池的方法引用,这个区域的作用就是将运行时常量池的符号引用转换成直接引用。

2.4 方法返回地址

方法返回地址也叫做方法正常退出或异常退出的定义。

方法返回地址存放的是调用该方法的程序计数器的值。程序计数器里面保存的是该线程要执行的下一行指令的位置

也就是说:在一个方法中调用了另一个方法,当被调用的方法执行完之后,要执行的下一行指令就是保存在此区域的。

2.5 附加信息

此区域在很多教程上会被省略,因为此区域有可能有数据,也有可能没有数据。这些附加信息是和 Java 虚拟机实现相关的一些信息。例如,对程序调试提供支持的信息。

总结

栈作为 Java 虚拟机中最核心的组成部分之一,它包含了以下 5 部分的内容:

  1. 局部变量表(Local Variables):主要存储的是方法内的基本数据类型和对象引用;
  2. 操作(数)栈(Operand Stack):主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间;
  3. 动态链接(Dynamic Linking):存放的是指向运行时常量池的方法引用;
  4. 方法返回地址(Return Address):存放的是调用该方法的程序计数器的值;
  5. 一些附加信息:存储了一些和 Java 虚拟相关的数据,比如程序的调试数据。

参考 & 鸣谢

《阿里巴巴Java开发手册》

《尚硅谷JVM》

本文已收录到 Gitee 开源仓库《Java 面试指南》,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。Java 面试有它就够了:超全 Java 常见面试题,持续更新...

对线面试官:浅聊一下 Java 虚拟机栈?的更多相关文章

  1. 【对线面试官】CountDownLatch和CyclicBarrier的区别

    <对线面试官>系列目前已经连载31篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  2. 【对线面试官】Kafka基础入门

    <对线面试官>系列目前已经连载33篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  3. 【对线面试官】Java NIO

    服务端: public class NoBlockServer { public static void main(String[] args) throws IOException { // 1.获 ...

  4. 【对线面试官】Java 反射&&动态代理

    // 抽象类,定义泛型<T> public abstract class BaseDao<T> { public BaseDao(){ Class clazz = this.g ...

  5. 【对线面试官】Java多线程基础

    // 请求直接交给线程池来处理 public void push(PushParam pushParam) { try { pushServiceThreadExecutor.submit(() -& ...

  6. 浅谈MySQL日志文件|手撕MySQL|对线面试官

    关注微信公众号[程序员白泽],进入白泽的知识分享星球 前言 上周五面试了字节的第三面,深感数据库知识的重要,我也意识到在平时的学习中,自己对于数据库的学习较为薄弱.甚至在有过一定实习经验之后,依旧因为 ...

  7. 面试官:为什么需要Java内存模型?

    面试官:今天想跟你聊聊Java内存模型,这块你了解过吗? 候选者:嗯,我简单说下我的理解吧.那我就从为什么要有Java内存模型开始讲起吧 面试官:开始你的表演吧. 候选者:那我先说下背景吧 候选者:1 ...

  8. MySQL优化篇(一),我可以和面试官多聊几句吗?——SQL优化流程与优化数据库对象

    我可以和面试官多聊几句吗?只是想偷点技能过来.MySQL优化篇(基于MySQL8.0测试验证),上部分:优化SQL语句.数据库对象,MyISAM表锁和InnoDB锁问题. MyISAM表锁和InnoD ...

  9. 【Java面试宝典】深入理解JAVA虚拟机

    一.运行时数据区域 线程隔离:线程隔离的意思,就是给不同的线程多分配的资源用,以做到不争用. 线程共享:线程共享就是资源只有一个没有办法分配更多,只能共享. Java虚拟机管理的内存包括几个运行时数据 ...

  10. Java虚拟机栈(java stack)

    虚拟机栈(java stack) 百度图片搜索里的动图搜索功能不错,可以搜索一些动图,展示操作数栈的操作过程,比较形象.这点google差点意思 虚拟机栈(jvm stacks)是线程独占的 里面是多 ...

随机推荐

  1. CF620E New Year Tree(树形+dfs序+线段树+状态压缩)

    题目链接 题目大意 \(~~\)给出一棵 nn 个节点的树,根节点为 11.每个节点上有一种颜色 c\(_{i}\) 和m 次操作.操作有两种: \(~~~~\) 1. 1\(~\)u\(~\)c:将 ...

  2. C#和Open eVision Studio图像库联合编程-读取图像

    OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "Image Files (*.t ...

  3. mindxdl--common--validators.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common this file ...

  4. 2.3 Goland快捷键

    1 新建 Alt + Insert Alt + Enter 2 移动 Alt + 上下箭头:以函数为单位移动 Ctrl + G : 定位到行 Ctrl + W :可以选择单词继而语句继而行继而函数Ct ...

  5. 启动homestead虚拟机 vagrant up执行后,提示Timed out while waiting for the machine to boot

    最近在启动homestead虚拟机时,总会卡在ssh验证这,几分钟后,就报timed out-- 以往都是重启电脑后,再次执行vagrant up后就能正常启动. 今日重启电脑很多次也无用. 查询解决 ...

  6. virtual继承和不继承

    用virtual修饰的虚函数,用来继承重写,没有virtual修饰的,取决于父类 定义 父类 *x=new 子类 首先父类不能调用子类的函数,自能调用本身的函数,所以,只有两种情况,1.无virtua ...

  7. xml中出现< >&等特殊字符如何存储

    特殊字符用下面对应得符号代替. < <= > >= & ' " < <= > >= & &apos; "

  8. RocketMQ系列-搭建Namesrv源码调试环境

    RocketMQ系列-搭建Namesrv源码调试环境 在学习任何一个技术框架的时候,我们通常都是先了解是什么,有什么作用.解决什么问题.设计亮点和设计思想是什么:当然对于技术学习上来说,这只是纸上谈兵 ...

  9. JavaEE Day09 JavaScript基础

    之前学了html.css两种静态资源 JavaScript是另一种静态资源,今日内容[重点]:JavaScript(是一门编程语言,2days)基础 一.JavaScript简介 1.概念 JavaS ...

  10. 【大数据面试】【框架】kafka:组成、台数/参数配置、持久化、ISR队列、宕机、丢数据、重复数据、数据积压、优化各种配置(刷盘、存盘、副本、压缩)、zk、其他

    一.基本信息 1.组成 生产者 broker 消费者 zookeeper:brokerid.consumer信息(不包含生产者的信息) 2.需要安装多少台 2 * (生产者的峰值生产速率 * 副本 / ...