对于 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. VBA---文件操作

    Text文件操作 Workbooks.OpenText() 载入一个文本文档,并将其作为包含单个工作表的新工作簿进行分列处理. 语法: 表达式.OpenText(Filename,StartRow, ...

  2. Oracle生成awr报告操作步骤介绍

    AWR全称Automatic Workload Repository,自动负载信息库,是Oracle 10g版本后推出的一种性能收集和分析工具,提供了一个时间段内整个系统的报表数据.通过AWR报告,可 ...

  3. SpringCloud(十) - Docker

    1.Docker安装 1.1 卸载旧版本(否者会安装出错) sudo yum remove docker \ docker-client \ docker-client-latest \ docker ...

  4. Azure DevOps Server 设置项目管理用户,用户组

    一,引言 Azure DevOps Server 搭建完成后,关于如何进行项目管理,项目成员管理等,我们接着上一篇文章,继续讲解 Azure DevOps Server 的用户,用户组.首先,我们需要 ...

  5. Go语言核心36讲50

    作为拾遗的部分,今天我们来讲讲与Go程序性能分析有关的基础知识. Go语言为程序开发者们提供了丰富的性能分析API,和非常好用的标准工具.这些API主要存在于: runtime/pprof: net/ ...

  6. 【Spring系列】- Spring事务底层原理

    Spring事务底层原理 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目录 Sprin ...

  7. 【每日一题】【map、数组、二维数组排序、静态函数和库函数】2022年2月24日-NC97 字符串出现次数的TopK问题

    描述给定一个字符串数组,再给定整数 k ,请返回出现次数前k名的字符串和对应的次数.返回的答案应该按字符串出现频率由高到低排序.如果不同的字符串有相同出现频率,按字典序排序.对于两个字符串,大小关系取 ...

  8. 一文理解什么是DevOps,通俗易懂白话文

    一文理解什么是DevOps,通俗易懂白话文 devops是什么❝DevOps维基百科定义 DevOps(Development和Operations的组合词)是一种重视"软件开发人员(Dev ...

  9. Go | 闭包的使用

    闭包基本介绍 闭包就是 一个函数 和其相关的 引用环境 组合的一个整体 好处: 保存引用的变量,下次继续使用,不会销毁 下面通过闭包的方式,写一个数字累加器,体验一下闭包的妙处 闭包实现数字累加 pa ...

  10. 什么是NineData?突然就火了

    NineData 是集成了 SQL 开发.数据复制.数据备份.数据对比多个模块的云服务,支持混合云(自建库+云数据库的业务架构)和多云(多个不同云厂商数据库组成的业务架构)架构下的企业数据管理,大幅降 ...