JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了运行时数据区、Java虚拟机栈等内容。

一、运行时数据区

Java虚拟机(JVM)在运行Java程序期间,会创建并维护一系列内存区域,这些区域总称为运行时数据区。这些区域根据其用途和特性,被严格定义并管理。《Java虚拟机规范》详细规定了这些区域的作用和行为,以确保所有Java虚拟机实现的一致性和正确性。

线程不共享区域:

  • 程序计数器:用于存储当前线程执行的字节码指令地址。这个区域是每个线程独有的,不共享。
  • Java虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,每个方法调用都会创建一个栈帧,用于存储局部变量、操作数栈、动态链接和方法出口信息。
  • 本地方法栈:与虚拟机栈相似,本地方法栈为native方法提供服务。

线程共享区域:

  • 方法区:用于存储已被JVM加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。
  • :堆是所有线程共享的区域,用于动态分配内存。所有的对象实例以及数组都应当在堆上分配。

image.gif

二、Java虚拟机栈

Java虚拟机栈是Java技术中至关重要的部分,它负责管理方法调用中的基本数据。在执行Java程序时,Java虚拟机栈是线程私有的,即每个线程都拥有自己的Java虚拟机栈,并且随着线程的创建而创建,随着线程的销毁而回收。


Java虚拟机栈,运用栈式数据结构对方法调用过程中的基本数据进行管理。遵循先进后出(First In Last Out)的原则,每一个方法调用都会对应一个独立的栈帧(Stack Frame)进行数据存储。

1.栈帧的组成

Java虚拟机栈的核心组成部分是栈帧,每一个方法的调用都会对应一个栈帧。这个栈帧存储了该方法的所有信息,包括局部变量、操作数栈、动态链接、方法出口等。

  • 局部变量表:这是在运行过程中存放所有局部变量(包括方法参数)的区域。在编译成字节码文件时,就可以确定局部变量表的内容。每个槽(slot)可以存放一个值,如果该值为long或double类型,则该槽会占用两个槽的空间。此外,实例方法的局部变量表中序号为0的位置存放的是this指针,它指向当前调用方法的对象。
  • 操作数栈:这是执行指令时存放临时数据的区域。操作数栈是实现中间计算结果交换和保存的主要场所,同时它也是函数调用的主要数据交互通道。函数作为参数传递时,就将其放在操作数栈上传递。
  • 帧数据:这部分主要包含动态链接、方法出口、异常表的引用等。动态链接保存了符号引用与运行时常量池的内存地址的映射关系。方法出口则指出了方法正常或异常结束时,当前栈帧应如何被弹出。异常表则是用于处理代码中异常的信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。

image.gif

2.局部变量表

局部变量表是存储在栈帧中的一块区域,它的作用是在方法执行过程中存放所有的局部变量。这些变量在编译成字节码文件时就可以确定其内容。

案例:

public class Demo1 {
    public static void test() {
        int i = 0;
        long j = 1;
    }
}
image.gif

字节码:

0 iconst_0
1 istore_0
2 lconst_1
3 lstore_1
4 return
image.gif

局部变量表(Nr.表示编号;起始PC、长度表示生效范围):

image.gif

案例分析图:

image.gif

栈帧中的局部变量表是一个数组,数组中的每个位置称之为槽(slot)。每个槽可以存放一个值,long和double类型的数据会占用两个槽的空间,其他类型的数据则占用一个槽。

局部变量表(序号表示槽的起始编号):

image.gif

在实例方法中,序号为0的位置专门用于存储当前调用方法的对象引用,即this指针。在运行时,该位置将存储实例对象的内存地址,以便于访问和操作对象的属性和方法。

案例:

public class Demo1 {
    public void test() {
        int i = 0;
        long j = 1;
    }
}
image.gif

局部变量表:

image.gif

方法参数也会保存在局部变量表中,其顺序与方法中参数定义的顺序一致。

局部变量表保存的内容有:实例方法的this对象、方法的参数、方法体中声明的局部变量等。为了节省空间,局部变量表中的槽是可以复用的,一旦某个局部变量不再生效,当前槽就可以再次被使用。

案例:

public class Demo1 {
    public void test(int x,int y) {
        int i = 0;
        long j = 1;
    }
}
image.gif

局部变量表:

image.gif

3.操作数栈

操作数栈是栈帧中虚拟机在执行指令过程中用来存放临时数据的一块区域。它是一种栈式的数据结构,如果一条指令将一个值压入操作数栈,则后面的指令可以弹出并使用该值。在编译期就可以确定操作数栈的最大深度,从而在执行时正确地分配内存大小。

案例:

public class Demo1 {
    public void test() {
        int i = 0;
        int j = i + 1;
    }
}
image.gif

字节码文件信息:

image.gif

字节码:

0 iconst_0
1 istore_1
2 iload_1
3 iconst_1
4 iadd
5 istore_2
6 return
image.gif

字节码分析:

0 iconst_0 将常量0放入操作数栈
1 istore_1 从操作数栈取出放入局部变量表1号位置
2 iload_1 将局部变量表1中的数据放入操作数栈
3 iconst_1 将常量1放入操作数栈
4 iadd 将操作数栈顶部的两个数据进行累加,结果放入栈中
5 istore_2 从操作数栈取出放入局部变量表2号位置
6 return 方法结束,返回

4.帧数据

当当前类的字节码指令引用了其他类的属性或方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址。动态链接就保存了编号到运行时常量池的内存地址的映射关系。这样在执行时就可以快速地找到对应的内存地址。

image.gif

方法出口指的是方法在正确或异常结束时,当前栈帧会被弹出,同时程序计数器应该指向上一个栈帧中的下一条指令的地址。所以在当前栈帧中,需要存储此方法出口的地址。这样在方法结束时,就可以快速地返回到上一个方法的执行位置。


异常表主要用于存储代码中异常的处理信息,它不仅涵盖了异常捕获的有效范围,还包含了try代码块和catch代码块执行后的字节码指令跳转位置。通过异常表,虚拟机能够精确地定位异常发生的位置,并采取适当的处理措施,从而确保程序的稳定性和可靠性。

案例:

public class Demo1 {
    public void test() {
        try {
            int i = 0;
        } catch (Exception e) {
            int j = 1;
        } finally {
            int k = 2;
        }
    }
}
image.gif

字节码文件信息:

image.gif

字节码:

0 iconst_0
 1 istore_1
 2 iconst_2
 3 istore_1
 4 goto 21 (+17)
 7 astore_1
 8 iconst_1
 9 istore_2
10 iconst_2
11 istore_1
12 goto 21 (+9)
15 astore_3
16 iconst_2
17 istore 4
19 aload_3
20 athrow
21 return
image.gif

总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了运行时数据区、Java虚拟机栈等内容,希望对大家有所帮助。

相关文章
|
10月前
|
存储 缓存 Java
我们来详细讲一讲 Java NIO 底层原理
我是小假 期待与你的下一次相遇 ~
335 2
|
9月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
280 0
|
11月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
345 0
|
10月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
752 58
|
9月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
483 24
|
8月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
628 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
10月前
|
XML JSON Java
Java 反射:从原理到实战的全面解析与应用指南
本文深度解析Java反射机制,从原理到实战应用全覆盖。首先讲解反射的概念与核心原理,包括类加载过程和`Class`对象的作用;接着详细分析反射的核心API用法,如`Class`、`Constructor`、`Method`和`Field`的操作方法;最后通过动态代理和注解驱动配置解析等实战场景,帮助读者掌握反射技术的实际应用。内容翔实,适合希望深入理解Java反射机制的开发者。
861 13
|
10月前
|
算法 Java 索引
说一说 Java 并发队列原理剖析
我是小假 期待与你的下一次相遇 ~
113 1
|
9月前
|
存储 运维 Kubernetes
Java启动参数JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
本文介绍了Java虚拟机(JVM)常用启动参数配置,包括设置初始堆内存(-Xms512m)、最大堆内存(-Xmx1024m)及内存溢出时生成堆转储文件(-XX:+HeapDumpOnOutOfMemoryError),用于性能调优与故障排查。
968 0