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

news2024/11/20 2:29:57

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_0
       1: invokespecial #1 // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: bipush        10
       2: bipush        20
       4: invokestatic  #2 // Method add:(II)I
       7: istore_1
       8: getstatic     #3 // Field java/lang/System.out:Ljava/io/PrintStream;
      11: new           #4 // class java/lang/StringBuilder
      14: dup
      15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
      18: ldc           #6 // String The sum is
      20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: iload_1
      24: 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;)V
      33: return

  public static int add(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: iadd
       3: istore_2
       4: iload_2
       5: 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.class
  Last modified 2024-9-13; size 706 bytes
  MD5 checksum fd7f75a854358ac89e66464a14018d24
  Compiled from "Example.java"
public class Example
  minor version: 0
  major version: 52
  flags: 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: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: bipush        10
         2: bipush        20
         4: invokestatic  #2                  // Method add:(II)I
         7: istore_1
         8: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: new           #4                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        18: ldc           #6                  // String The sum is
        20: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        23: iload_1
        24: 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;)V
        33: return
      LineNumberTable:
        line 3: 0
        line 4: 8
        line 5: 33

  public static int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=2
         0: iload_0
         1: iload_1
         2: iadd
         3: istore_2
         4: iload_2
         5: ireturn
      LineNumberTable:
        line 7: 0
        line 8: 4
}
SourceFile: "Example.java"

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

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

Classfile /D:/Example.class
  Last modified 2024-9-13; size 706 bytes
  MD5 checksum fd7f75a854358ac89e66464a14018d24
  Compiled from "Example.java"

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

public class Example
  minor version: 0
  major version: 52
  flags: 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.coloradmin.cn/o/2166139.html

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

相关文章

Python办公自动化教程(004):PDF添加水印

1.4 PDF文档水印添加 【1】安装库 pip install reportlab pip install PyPDF2【2】代码 import iofrom PyPDF2 import PdfWriter, PdfReader from reportlab.lib import pagesizes # 页面样式 from reportlab.lib.units import cm from reportlab.pdfbase import pdfmetric…

【Verilog学习日常】—牛客网刷题—Verilog企业真题—VL68

同步FIFO 描述 请设计带有空满信号的同步FIFO&#xff0c;FIFO的深度和宽度可配置。双口RAM的参考代码和接口信号已给出&#xff0c;请在答案中添加并例化此部分代码。 电路的接口如下图所示。端口说明如下表。 接口电路图如下&#xff1a; 双口RAM端口说明&#xff1a; 端口…

828华为云征文|使用Flexus X实例集成ES搜索引擎

目录 一、应用场景 1.1 Flexus X实例概述 1.2 ES搜索引擎 二、安装相关服务 2.1 安装Elasticsearch7.17.0 2.2 安装kibana7.17.0 三、开通安全组规则 四、整体感受 4.1 Flexus X实例 4.2 使用感觉 一、应用场景 1.1 Flexus X实例概述 Flexus X实例是华为云推出的一款…

Windows内核编程基础(2)

上下文环境 应用层应用程序工作在用户模式&#xff0c;内核驱动程序工作在内核模式。这里的用户模式和内核模式是基于CPU的特权环来定义的&#xff0c;CPU提供了0环~3环(ring 0 ~ ring 3)共四个特权环&#xff0c;Windows操作系统使用了其中的0环和3环&#xff0c;0环为内核模…

【深度学习】(7)--保存最优模型

文章目录 保存最优模型一、两种保存方法1. 保存模型参数2. 保存完整模型 二、迭代模型 总结 保存最优模型 我们在迭代模型训练时&#xff0c;随着次数初始的增多&#xff0c;模型的准确率会逐渐的上升&#xff0c;但是同时也随着迭代次数越来越多&#xff0c;由于模型会开始学…

大数据-148 Apache Kudu 从 Flink 下沉数据到 Kudu

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Spring Boot房屋租赁平台:现代化解决方案

1 绪论 1.1 研究背景 中国的科技的不断进步&#xff0c;计算机发展也慢慢的越来越成熟&#xff0c;人们对计算机也是越来越更加的依赖&#xff0c;科研、教育慢慢用于计算机进行管理。从第一台计算机的产生&#xff0c;到现在计算机已经发展到我们无法想象。给我们的生活改变很…

Recaptcha2 图像识别 API 对接说明

Recaptcha2 图像识别 API 对接说明 本文将介绍一种 Recaptcha2 图像识别2 API 对接说明&#xff0c;它可以通过用户输入识别的内容和 Recaptcha2验证码图像&#xff0c;最后返回需要点击的小图像的坐标&#xff0c;完成验证。 接下来介绍下 Recaptcha2 图像识别 API 的对接说…

8.12DoG (Difference of Gaussians)

基本概念 不同尺度的高斯模糊图像之间的差异&#xff08;DoG&#xff09;&#xff0c;用于边缘检测。函数: cv::GaussianBlur() 结合 cv::Laplacian() 或者自定义DoG实现。 在OpenCV中并没有直接提供一个名为“DoG”&#xff08;Difference of Gaussians&#xff09;的函数&a…

【学术会议征稿】第四届人工智能、机器人和通信国际会议(ICAIRC 2024)

第四届人工智能、机器人和通信国际会议&#xff08;ICAIRC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Robotics, and Communication 第四届人工智能、机器人和通信国际会议&#xff08;ICAIRC 2024&#xff09;定于2024年12月27-29日…

css 自定义滚动条样式

* { scrollbar-color: auto !important; scrollbar-width: auto; } //滚动条宽高 ::-webkit-scrollbar { width: 4px; height: 4px; background: transparent; } ::-webkit-scrollbar-thumb { //滑块部分 border-radius: 5px; background-color: rgba(32, 224, 254, 1); } ::-…

【Python报错已解决】TypeError: can only concatenate str (not “float“) to str

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

docker compose的使用

docker compose 1.概述 是 Docker 官方提供的一款开源工具&#xff0c;主要用于简化在单个主机上定义和运行多容器 Docker 应用的过程。它的核心作用是容器编排&#xff0c;使得开发者能够在一个统一的环境中以声明式的方式管理多容器应用的服务及其依赖关系。 也就是说Docker…

用 Django 5 快速生成一个简单 进销存 系统 添加 个打印 按钮

一、前置条件&#xff1a; 1.安装好python 【关联网址】 2. 安装好vscode 【关联网址】 插件 3. 登陆海螺AI【关联网址】 4. 安装好 pip install django 【关联网址】 pip install django -i https://mirrors.aliyun.com/pypi/simple/ 二、开始生成 1. 打开vscode 打开…

[数据库实验五] 审计及触发器

一、实验目的与要求&#xff1a; 1.了解MySQL审计功能及实现方式 2.掌握触发器的工作原理、定义及操作方法 二、实验内容&#xff1a; 注&#xff1a; 在同一个触发器内编写多行代码&#xff0c;需要用结构begin ……end 函数current_user()获得当前登录用户名 1.自动保存…

Linux 应用层自定义协议与序列化

文章目录 一、应用层1、协议2、序列化 && 反序列化3、通过Json库进行数据的序列化 && 反序列化Json::Value类Json::Reader类Json::Writer类 二、为什么read、write、recv、send和Tcp支持全双工&#xff1f;发数据的本质&#xff1a;tcp支持全双工通信的原因&am…

gitlab-runner集成CI/CD完整项目部署

目录 1.环境安装 2.gitlab代码仓库搭建 3.gitlab-runner-安装以及注册 4..gitlab-ci.yml脚本 5.脚本说明 6.build.sh 7.test.sh 8. deploy.sh 9.运行流水线 10.选择流水线分支 11.查看运行阶段 12.查看运行日志 13.查看服务器真实日志 1.环境安装 确保服务器的Java环…

Python_异常机制

软件程序在运行过程中&#xff0c;非常可能遇到刚刚提到的这些问题&#xff0c;我们称之为异常&#xff0c;英文是&#xff1a;Exception&#xff0c;意思是例外。遇到这些例外情况&#xff0c;或者叫异常&#xff0c;我们怎么让写的程序做出合理的处理&#xff0c;安全的退出&…

Footprint Growthly Quest 工具:赋能 Telegram 社区实现 Web3 飞速增长

作者&#xff1a;Stella L (stellafootprint.network) 在 Web3 的快节奏世界里&#xff0c;社区互动是关键。而众多 Web3 社区之所以能够蓬勃发展&#xff0c;很大程度上得益于 Telegram 平台。正因如此&#xff0c;Footprint Analytics 精心打造了 Growthly —— 一款专为 Tel…

Tkinter制作登录界面以及登陆后页面切换

Tkinter制作登录界面以及登陆后页面切换 前言序言1. 由来2. 思路3. 项目结构描述4. 项目实战1. 登录界面实现&#xff08;代码&#xff09;2. 首页界面实现&#xff08;代码&#xff09;3. 打包build.py&#xff08;与main.py同级目录&#xff09;4. 打包安装包 前言 本帖子&a…