当前位置: 首页 > article >正文

java内存区域 - 栈

目录

  • java内存区域 - 栈
      • 1. Java虚拟机栈的组成
      • 2. 栈帧中的详细内容
        • 2.1 局部变量表
        • 2.2 操作数栈
        • 2.3 动态链接
        • 2.4 方法返回地址
        • 2.5 附加信息
      • 3. JVM栈的生命周期
      • 4. 示例解析 - 运行时的栈帧分布
      • 5. 栈中的异常
      • 6.栈配置
      • 7.本地方法栈

java内存区域 - 栈

在JDK11中,JVM栈 (Java Virtual Machine Stack)是每个线程私有的内存区域,用于存储线程执行方法时的方法调用状态。JVM栈也称为Java虚拟机栈,与线程的生命周期相同。

以下是对Java虚拟机栈的详细解析,包括它的组成部分和存储内容:


1. Java虚拟机栈的组成

Java虚拟机栈由一个个**栈帧(Stack Frame)**组成。每次调用一个方法时,都会创建一个新的栈帧并压入JVM栈,方法执行完成后,栈帧会出栈。

一个栈帧包含以下几个主要区域:

  1. 局部变量表(Local Variable Table)
  2. 操作数栈(Operand Stack)
  3. 动态链接(Dynamic Linking,也叫方法引用)
  4. 返回地址(Return Address)
  5. 额外信息(Optional Data,如异常处理状态)

2. 栈帧中的详细内容

2.1 局部变量表
  • 定义: 局部变量表是用来存储方法中的局部变量,包括:

    • 方法的参数
    • 方法内部定义的局部变量
  • 存储内容:

    • 基本数据类型(如 intfloatlongdouble 等)
    • 引用类型(对象引用,如 String 的引用)
    • returnAddress 类型(指向字节码指令地址)
  • 特性:

    • 局部变量表的大小在编译时确定,且在方法调用时分配。
    • 表的索引从 0 开始,方法的第一个参数通常存储在索引 0。
2.2 操作数栈
  • 定义: 操作数栈是一个后进先出(LIFO)的栈,用于方法执行中的中间运算和结果存储。

  • 存储内容:

    • 方法执行过程中需要操作的中间数据(如加法操作中的两个操作数)。
    • 方法调用的参数(当调用其他方法时)。
  • 特性:

    • 字节码指令直接对操作数栈进行操作,比如 iadd 用于弹出两个整数值相加并将结果压入栈。
2.3 动态链接
  • 定义: 动态链接存储的是方法调用过程中的符号引用,这些符号引用在运行时被解析为具体的方法或字段的直接引用。

  • 作用:

    • 支持方法调用的动态绑定,尤其是多态。
    • 通过运行时常量池解析相关符号。
2.4 方法返回地址
  • 定义: 返回地址用于存储方法调用完成后,需要返回的程序计数器(PC)位置。

  • 特性:

    • 如果当前方法是通过字节码指令调用的,返回地址会指向调用指令的下一条指令。
    • 如果方法调用是通过其他方式(如异常),返回地址可能为空。
2.5 附加信息
  • 定义: 栈帧可能还包含一些与方法执行相关的附加信息,如:
    • 异常处理表
    • 调试信息

3. JVM栈的生命周期

  • 每个线程创建时,Java虚拟机栈被分配。
  • Java虚拟机栈的每个栈帧对应一个方法调用,随着方法调用的嵌套,栈帧逐步压入栈中。
  • 每一个方法冲开始调用到执行结束的过程,都对应这一个栈帧的入栈出栈的过程。
  • 当方法返回或异常终止时,栈帧出栈。
  • 如果栈深度超过允许的最大值(如无限递归),会抛出 StackOverflowError
  • 如果内存不足以为新栈帧分配空间,会抛出 OutOfMemoryError

4. 示例解析 - 运行时的栈帧分布

以下是一个简单的代码示例,以及在JVM栈中的体现:

public class Test {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int sum = add(a, b);
    }

    public static int add(int x, int y) {
        return x + y;
    }
}
  1. main 方法调用:

    • 局部变量表:
      • args:存储数组引用
      • a:5
      • b:10
      • sum:等待存储 add 方法返回的结果
    • 操作数栈:
      • 调用 add 方法时,将 ab 压入操作数栈。
  2. add 方法调用:

    • 局部变量表:
      • x:存储值 5
      • y:存储值 10
    • 操作数栈:
      • 执行 x + y 时,先将 xy 压入栈,再弹出进行加法操作。
  3. 返回:

    • add 方法将结果压入操作数栈,并返回给 main 方法的 sum 变量。

5. 栈中的异常

  • StackOverflowError:

    • 发生在栈的深度超过虚拟机允许的最大值时,例如递归调用过深。
  • OutOfMemoryError:

    • 如果JVM栈内存允许动态扩展,当扩展栈容量无法申请到足够内存时, 无法为新的栈帧分配内存时触发。
  • 注意: 《Java虚拟机规范》明确允许Java虚拟机实现自行选择是否支持站的动态扩展,而Hotspot(jdk1.2及之后的默认虚拟机)的选择是不支持扩展,所以jdk1.2之后,除非在创建线程申请内存时就因为无法获得足够的内存而触发OutOfMemoryError异常,否则在线程运行时是不会因为扩展而导致内存的溢出的,只会因为栈容量无法容纳新的栈帧而导致StackOverflowError异常(此段摘自《深入理解Java虚拟机》 第第三版中)


6.栈配置

  • -Xss<size>
    • 用于设置每个线程栈的大小。
    • 参数格式为:
      • <size> 指定大小,可以用单位 km 表示(如 -Xss512k-Xss1m)。
      • 默认值依赖于操作系统和JVM实现,通常为 1 MB 或 512 KB。
    • 影响每个线程的栈深度,但总栈大小受限于系统内存和线程数。

7.本地方法栈

  • 本地方法栈(Natice Method Stacks) 与Java虚拟机栈所发挥的作用时非常相似的,其区别只是Java虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则是为了虚拟机使用到的本地(native)方法服务。HotSpot虚拟机(Java默认的虚拟机)直接把本地方法栈和虚拟机栈合二为一了。

总结:
JVM栈的核心在于管理方法调用和执行,每个栈帧包括局部变量表、操作数栈、动态链接、返回地址等。栈的合理设计是 Java 程序高效运行的基础,同时需要注意栈的溢出问题。


http://www.kler.cn/a/473152.html

相关文章:

  • 继承(6)
  • 多模态大模型初探索:通过ollama部署多模态大模型
  • 【Qt】C++11 Lambda表达式
  • 使用WPF在C#中制作下载按钮
  • 什么是 ES6 “模板语法” ?
  • 【HarmonyOS NEXT】鸿蒙应用使用后台任务之长时任务,解决屏幕录制音乐播放等操作不被挂起
  • 如何用Python编程实现自动整理XML发票文件
  • 从零开始:构建一个简单的聊天应用使用 WebSocket 和 React Native
  • Clojure语言的学习路线
  • Erlang语言的函数实现
  • 国内大带宽服务器的应用场景
  • DeepSeek-V3 通俗详解:从诞生到优势,以及与 GPT-4o 的对比
  • 前端VUE首次加载错误类型
  • CSS——24.实战技能网导航栏 hove状态
  • docker搭建atlassian-confluence:7.2.0
  • MySQL学习笔记(二)
  • element-ui中多个表单el-form进行显示/隐藏切换时表单部分校验失效的解决办法
  • 服务器漏洞修复解决方案
  • Chapter 4.5:Connecting attention and linear layers in a transformer block
  • 【VUE 指令学习笔记】
  • Linux/Ubuntu/银河麒麟 arm64 飞腾FT2000 下使用 arm64版本 linuxdeployqt 打包Qt程序
  • MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合
  • 红黑树详解
  • 极客公园创新大会探索AI未来,Soul App创始人张璐团队以技术驱动创新
  • 百济神州后端开发工程师 - 部分笔试题 - 解析
  • spark——RDD算子集合