【JVM原理】运行时数据区(内存结构)

JVM (Java Virtual Machine)原理

文章目录

  • 四、运行时数据区(内存结构)
    • 4-1 线程私有区域
      • 程序计数器(program counter Register)
      • 本地方法栈(Native Method Stacks)
      • Java 虚拟机栈(Java Virtual Machine Stacks)
        • 局部变量表(Local Variable Table)
        • 操作数栈(Operand Stack)
        • 动态链接(Dynamic Linking)
        • 方法返回地址(Return Address)
    • 4-2 线程共享区域
      • 方法区(Method Area)
      • 运行时常量池(Run-Time Constant Pool)
      • 堆(Heap)

前情提要:
如果你还没看过【JVM原理】类加载机制 请跳转阅读


四、运行时数据区(内存结构)

oracle官方文档:

  • Chapter 2. The Structure of the Java Virtual Machine (oracle.com)

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。

4-1 线程私有区域

在这里插入图片描述

程序计数器(program counter Register)

程序计数器是一个较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每个线程都有一个独立的程序计数器,用于记录线程当前执行的位置。

本地方法栈(Native Method Stacks)

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。一些虚拟机(如 HotSpot)直接把本地方法栈和虚拟机栈合二为一。

Java 虚拟机栈(Java Virtual Machine Stacks)

它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储 局部变量表(Local Variable Table)、操作数栈(Operand Stack)、动态链接(Dynamic Linking)、方法返回地址(Return Address)等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  • 动态链接:在程序运行阶段,由符号引用转化为直接引用
  • 静态链接:在解析阶段,由符号引用转为直接引用

假设我们有一个简单的 Java 方法,该方法接收两个整数参数,计算它们的和,并返回结果:

public class Example {public static void main(String[] args) {int sum = add(10, 20);System.out.println("The sum is " + sum);}public static int add(int a, int b) {int result = a + b;return result;}
}

编译 Java 源代码并查看生成的字节码的命令

javac Example.java
javap -c Example.class > Example.txt
Compiled from "Example.java"
public class Example {public Example();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: bipush        102: bipush        204: invokestatic  #2 // Method add:(II)I7: istore_18: getstatic     #3 // Field java/lang/System.out:Ljava/io/PrintStream;11: new           #4 // class java/lang/StringBuilder14: dup15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V18: ldc           #6 // String The sum is20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;23: iload_124: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;27: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;30: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V33: returnpublic static int add(int, int);Code:0: iload_01: iload_12: iadd3: istore_24: iload_25: ireturn
}

逐行解释

Example 类的构造方法:

aload_0加载this引用到操作数栈中。
invokespecial #1调用Object类的构造函数<init>,初始化this对象。
return返回,构造方法结束。

main 方法的字节码:

// 加载整数
0: bipush 10 将整数10压入操作数栈。
2: bipush 20 将整数20压入操作数栈。// 调用静态方法
3: invokestatic #2 调用静态方法add,该方法接收两个整数参数并返回一个整数。此时,操作数栈中有两个整数(20和10),它们作为参数传递给add方法。// 存储结果
7: istore_1 将add方法的返回值(即两个整数相加的结果)存储到局部变量表的第二个槽(索引1)中。8: getstatic #3 加载System.out字段的引用到操作数栈。// 创建 StringBuilder 对象
11: new #4 创建一个新的StringBuilder对象,并将引用压入栈顶。
14: dup 复制栈顶的StringBuilder对象引用,使得栈中有两个引用。
15: invokespecial #5 调用StringBuilder的构造函数初始化对象。// 附加字符串
18: ldc #6 将字符串"The sum is "加载到栈顶。
20: invokevirtual #7 调用StringBuilder对象的append方法,将字符串附加到StringBuilder对象上。// 附加整数
23: iload_1 从局部变量表的第二个槽(索引1)中加载add方法的返回值。
24: invokevirtual #8 调用StringBuilder.append(int)方法,将整数附加到StringBuilder对象上。// 转换为字符串并打印
27: invokevirtual #9 调用StringBuilder.toString()方法,将StringBuilder对象转换为字符串,并将字符串压入栈顶。
30: invokevirtual #10 调用PrintStream对象的println方法,打印栈顶的字符串。
33: return 结束方法。

StringBuilder对象的操作:"The sum is "是从常量池加载到栈顶,然后与从局部变量表加载的整数结果拼接在一起,形成最终的输出字符串。

其中的数字表示程序计数器的指向,如 2: bipush20 表示执行完 bipush 10 后,程序计数器更新为 2

add方法的字节码:

0: iload_0 将局部变量表中索引0位置的第一个整数参数加载到栈顶。
1: iload_1 将局部变量表中索引1位置的第二个整数参数加载到栈顶。
2: iadd 取栈顶的两个整数相加,并将结果压回栈顶。
3: istore_2 将栈顶的整数结果存储到局部变量表的索引2位置。
4: iload_2 再次将局部变量表中索引2位置的整数加载到栈顶。
5: ireturn 返回栈顶的整数值。

输出详细的类文件信息

javap -v Example.class > dynamiclink.txt
Classfile /D:/Example.classLast modified 2024-9-13; size 706 bytesMD5 checksum fd7f75a854358ac89e66464a14018d24Compiled from "Example.java"
public class Exampleminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #12.#23        // java/lang/Object."<init>":()V#2 = Methodref          #11.#24        // Example.add:(II)I#3 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;#4 = Class              #27            // java/lang/StringBuilder#5 = Methodref          #4.#23         // java/lang/StringBuilder."<init>":()V#6 = String             #28            // The sum is#7 = Methodref          #4.#29         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#8 = Methodref          #4.#30         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;#9 = Methodref          #4.#31         // java/lang/StringBuilder.toString:()Ljava/lang/String;#10 = Methodref          #32.#33        // java/io/PrintStream.println:(Ljava/lang/String;)V#11 = Class              #34            // Example#12 = Class              #35            // java/lang/Object#13 = Utf8               <init>#14 = Utf8               ()V#15 = Utf8               Code#16 = Utf8               LineNumberTable#17 = Utf8               main#18 = Utf8               ([Ljava/lang/String;)V#19 = Utf8               add#20 = Utf8               (II)I#21 = Utf8               SourceFile#22 = Utf8               Example.java#23 = NameAndType        #13:#14        // "<init>":()V#24 = NameAndType        #19:#20        // add:(II)I#25 = Class              #36            // java/lang/System#26 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;#27 = Utf8               java/lang/StringBuilder#28 = Utf8               The sum is#29 = NameAndType        #39:#40        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#30 = NameAndType        #39:#41        // append:(I)Ljava/lang/StringBuilder;#31 = NameAndType        #42:#43        // toString:()Ljava/lang/String;#32 = Class              #44            // java/io/PrintStream#33 = NameAndType        #45:#46        // println:(Ljava/lang/String;)V#34 = Utf8               Example#35 = Utf8               java/lang/Object#36 = Utf8               java/lang/System#37 = Utf8               out#38 = Utf8               Ljava/io/PrintStream;#39 = Utf8               append#40 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;#41 = Utf8               (I)Ljava/lang/StringBuilder;#42 = Utf8               toString#43 = Utf8               ()Ljava/lang/String;#44 = Utf8               java/io/PrintStream#45 = Utf8               println#46 = Utf8               (Ljava/lang/String;)V
{public Example();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=3, locals=2, args_size=10: bipush        102: bipush        204: invokestatic  #2                  // Method add:(II)I7: istore_18: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;11: new           #4                  // class java/lang/StringBuilder14: dup15: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V18: ldc           #6                  // String The sum is20: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;23: iload_124: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;27: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;30: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V33: returnLineNumberTable:line 3: 0line 4: 8line 5: 33public static int add(int, int);descriptor: (II)Iflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=20: iload_01: iload_12: iadd3: istore_24: iload_25: ireturnLineNumberTable:line 7: 0line 8: 4
}
SourceFile: "Example.java"

javap -v Example.class 输出的内容包括了

​ ① 类文件的基本信息,如:文件路径、最后修改日期、文件大小、MD5校验和及编译源文件。

Classfile /D:/Example.classLast modified 2024-9-13; size 706 bytesMD5 checksum fd7f75a854358ac89e66464a14018d24Compiled from "Example.java"

​ ② 类定义部分描述了类的基本属性如:minor version次版本号、major version主版本号等。

public class Exampleminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER

​ ③ Constant pool 常量池用于存储类定义中用到的各种常量,包括符号引用(如类和接口的全限定名、字段名和方法名)、常量值(如字符串和数字)等。每一行代表一个常量池条目,编号从#1开始。例如,#1是一个方法引用,指向java/lang/Object类的"<init>"方法,其描述符为()V,表示这是一个无参数无返回值的方法。

​ ④ 接下来的部分展示了类中的方法定义。包括:构造方法、main 方法和 add 方法。

在理解了字节码代表含义的基础上,下面逐一解释在这个例子中的局部变量表、操作数栈、动态链接以及方法返回地址的工作原理。

局部变量表(Local Variable Table)

局部变量表是用来存储方法内部使用的局部变量的数据结构。每个方法都有自己的局部变量表,它是一组变量槽(slots)的集合,每个槽可以存储一个Java虚拟机基本类型的值(如int, float, reference等)或者一个对象引用。

构造方法中

locals=1 表示局部变量表有1个槽。
args_size=1 表示构造方法有一个参数(this引用)。
0: aload_0 加载this引用,0是局部变量表中的第一个槽,这里存放的是this引用。

main 方法中

locals=2 表示局部变量表有两个槽。
args_size=1 表示main方法有一个参数(String[] args)该参数存储于局部变量表的第1个槽中
0: bipush 10 和 2: bipush 20 不直接涉及局部变量表,它们是将整数直接压入操作数栈。
7: istore_1 将操作数栈顶部的整数结果存储到局部变量表的第2个槽中。
操作数栈(Operand Stack)

操作数栈是一个用于暂存数据的后进先出(LIFO)栈。在方法执行过程中,操作数栈用于临时存放中间计算结果和操作数。

main 方法

0: bipush 10 将整数10压入操作数栈。
2: bipush 20 将整数20压入操作数栈。
4: invokestatic #2 调用静态方法 add,从栈中弹出两个整数参数,并将返回的结果(整数和)压入栈中。
7: istore_1 将栈顶的整数结果存储到局部变量表中。
动态链接(Dynamic Linking)

动态链接是指在类文件被加载到JVM时,JVM 解析常量池中的符号引用为直接引用的过程。例如,当执行invokestatic #2指令时,JVM 需要知道 add 方法的具体位置。这需要 JVM 根据 #2 的符号引用 Example.add:(II)I 解析出实际的方法引用,并准备好调用该方法。

方法返回地址(Return Address)

方法退出指的是方法执行完毕,控制权返回给调用者的过程。在字节码中,通常通过 return 指令来表示方法的结束。

  • 在构造方法中,4: return 指令表示构造方法执行完成。
  • main方法中,33: return 指令表示main方法执行完成。
  • add方法中,5: ireturn 指令表示方法执行完成,并返回一个整数值。

总结来说,局部变量表用于存储方法中的局部变量,操作数栈用于临时存储计算过程中的数据,动态链接用于解析方法调用时的符号引用,而方法返回地址则是通过特定的返回指令来表示方法的结束。

4-2 线程共享区域

方法区(Method Area)

方法区(也称为非堆区)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 “Non-Heap” (非堆),目的是与 Java 堆区分开来。方法区不需要连续的内存空间,也有关于它是否固定大小或者可扩展的选择,甚至可以选择不实现垃圾回收。(jdk1.8 以前 hotspot虚拟机叫永久代、持久代, jdk1.8 时叫元空间)

运行时常量池(Run-Time Constant Pool)

Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。对于常量池中的各种字符串字面量和符号引用,虚拟机提供了特定的方法给予创建和访问。

堆(Heap)

在这里插入图片描述

堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap)。从内存回收的角度来看,堆可以细分为年轻代(Young Generation)和老年代(Old Generation)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147287.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【可变模板参数】

文章目录 可变参数模板的概念可变参数模板的定义方式参数包的展开方式递归展开参数包逗号表达式展开参数包 STL容器中的emplace相关接口函数 可变参数模板的概念 可变参数模板是C11新增的最强大的特性之一&#xff0c;它对参数高度泛化&#xff0c;能够让我们创建可以接受可变…

C++ | Leetcode C++题解之第423题从英文中重建数字

题目&#xff1a; 题解&#xff1a; class Solution { public:string originalDigits(string s) {unordered_map<char, int> c;for (char ch: s) {c[ch];}vector<int> cnt(10);cnt[0] c[z];cnt[2] c[w];cnt[4] c[u];cnt[6] c[x];cnt[8] c[g];cnt[3] c[h] - …

C++初阶学习——模版进阶

1. 非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成…

GroupMamba: Parameter-Efficient and Accurate Group Visual State Space Model

GroupMamba: Parameter-Efficient and Accurate Group Visual State Space Model 1.介绍2.相关工作3.方法3.1预备知识3.2整体架构3.3调制组Mamba层3.3.1视觉单一选择性扫描&#xff08;Visual Single Selective Scan&#xff0c;简称VSSS&#xff09;块3.3.2分组Mamba操作3.3.3…

Vue 计算属性(computed)的使用和优化

前言 Vue 的计算属性&#xff08;computed&#xff09;是 Vue.js 中非常重要的一个概念。它允许你在 Vue 组件中定义一些计算逻辑&#xff0c;这些逻辑会依赖于 Vue 的响应式数据&#xff0c;并且具有缓存机制&#xff0c;只有在依赖数据发生改变时才会重新计算&#xff0c;从…

蓝桥杯—STM32G431RBT6(ADC数模转换,从原理到应用)

一、什么是ADC&#xff1f; ADC&#xff08;Analog-to-Digital Converter&#xff09;即模数转换器。它是一种将模拟信号转换为数字信号的电子器件。在电子系统中&#xff0c;ADC 起着至关重要的作用&#xff0c;它能将连续变化的模拟量&#xff08;如电压、电流等&#xff09;…

ps学习。

有大量的图要扣&#xff0c;淘宝5-15块扣一个&#xff0c;尽管蛮便宜的&#xff0c;但是架不住量大啊&#xff0c;还是好好ps&#xff0c;也能省一大笔钱。 填充 在这里有个油漆桶&#xff0c;一开始也叫渐变色&#xff0c;堆放在一起了&#xff0c;我觉得这不是个好设计。。…

pycharm 使用 translation 插件通过openai进行翻译

pycharm 使用 translation 插件通过openai进行翻译 1. 安装插件2. 配置插件3. 翻译 1. 安装插件 2. 配置插件 3. 翻译 调用 openai 时使用的提示词如下&#xff1a; <|im_start|>system\nYou are a translation engine that can only translate text and cannot interpr…

Vue学习记录之七(组件之间传参)

一、父传子 1、父组件传递 父&#xff1a; App.vue&#xff0c; 通过使用组件 <导入的组件名 :属性名1“” :属性名2“”></导入的组件名>,传递给子组件 传递了一个t字符串类型是不需要v-bind&#xff0c;也就是不需要冒号&#xff0c;非字符串类型的必须加 v-bi…

CTC loss 博客转载

论文地址&#xff1a; https://www.cs.toronto.edu/~graves/icml_2006.pdf 为了对应这个图&#xff0c;我们假设一种符合的模型情况&#xff1a; 英文OCR&#xff0c;37个类别&#xff08;26个小写字母10个汉字空格&#xff09;&#xff0c;最大输出长度8个字符 模型预测结果…

PCL 计算点云的平均密度(方法一)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 本文将介绍如何计算点云的…

如何避开学习和研究机器人方向无价值的知识节约时间

往昔 这是一篇十年前就想写&#xff0c;但是一直没有实力和勇气落笔的文字。 如今 简约 授之以鱼&#xff0c;不如授之以渔。 啰嗦 机器人方向如何简单判定这个知识是否有价值。 只谈一个方向&#xff0c;就是这个知识点是“死”还是“活”&#xff1f; 什么是“死”&am…

element-ui表格操作大全

一、基础表格展示 数据绑定&#xff1a; 在el-table元素中注入data对象数组&#xff0c;在el-table-column&#xff08;列&#xff09;中使用prop属性来对应对象中的键名&#xff0c;使用label属性定义列名 元素案例内容&#xff1a; <el-table border :data"userL…

举例说明:自然语言处理实战项目

自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;旨在使计算机能够理解、解释和生成人类语言。以下是一些NLP实战项目的示例&#xff1a; 1. 情感分析&#xff08;Sentiment Analysis&#xff09; 项目描述: …

【LLM学习之路】9月16日 第六天

【LLM学习之路】9月16日 第六天 损失函数 L1Loss 可以取平均也可以求和 参数解析 input &#xff08;N&#xff0c;*&#xff09; N是batchsize&#xff0c;星号代表可以是任意维度 不是输入的参数&#xff0c;只是描述数据 target 形状要同上 MSELoss平方差 CrossEntr…

(done) 声音信号处理基础知识(5) (Types of Audio Features for Machine Learning)

参考&#xff1a;https://www.youtube.com/watch?vZZ9u1vUtcIA 声学特征描述了声音&#xff0c;不同特征捕捉声音的不同方面性质 声学特征有助于我们构建智能声学系统 声学特征分类有&#xff1a; 1.抽象等级 2.时域视野 3.音乐的部分 4.信号域 5.机器学习方法 如下图展示…

力扣中等 33.搜索旋转排序数组

文章目录 题目介绍题解 题目介绍 题解 首先用 153. 寻找旋转排序数组中的最小值 的方法&#xff0c;找到 nums 的最小值的下标 i。 然后分类讨论&#xff1a; 如果 target>nums[n−1]&#xff0c;在 [0,i−1] 中二分查找 target。 如果 target≤nums[n−1]&#xff0c;那…

51单片机——独立按键

一、独立按键对应单片机P3管脚&#xff0c;如图 二、按键点亮LED灯 #include <STC89C5xRC.H> void main() { while(1) { if(P300) { P200; } else { P201; } } } 当按键为0时&#xff0c;代表按下&#xff0c;所以当P30按下时&#xff0c;让P20&#xff1d;0&#…

二叉树(二)深度遍历和广度遍历

一、层序遍历 广度优先搜索&#xff1a;使用队列&#xff0c;先进先出 模板&#xff1a; 1、定义返回的result和用于辅助的队列 2、队列初始化&#xff1a; root非空时进队 3、遍历整个队列&#xff1a;大循环while(!que.empty()) 记录每层的size以及装每层结果的变量&a…

leetcode第十三题:罗马数字转整数

罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#x…