Java内存区域速览

文章目录

  • JVM的组成
    • 加载字节码流程
  • 运行时数据区-总览
  • 1. 程序计数器
  • 2. 虚拟机栈
    • 栈帧
    • 栈的运行原理
  • 3. 本地方法栈
  • 4. 堆内存(Java Heap
    • 虚拟机对堆 的划分
      • 1. 年轻代(Young Generation):
      • 2. 老年代(Old Generation):
      • 3. 永久代/元空间(Permanent Generation/Metaspace):
      • 注意事项:
    • 对象在堆中的生命周期
      • 1. **创建阶段(Creation):**
      • 2. **引用阶段(Reference):**
      • 3. **使用阶段(Usage):**
      • 4. **不可达阶段(Unreachable):**
      • 5. **垃圾收集阶段(Garbage Collection):**
      • 注意事项:
  • 5. 方法区(Method Area
    • 主要存储

JVM的组成

JVM 指的是 Java 虚拟机(Java Virtual Machine),它是 Java 程序的运行环境。JVM 可以在不同的操作系统上执行 Java 字节码,这使得 Java 程序具有跨平台性,只需要编写一次,就可以在任何安装了 Java 运行时环境(JRE)的地方运行。

主要包括以下几个方面:

  1. 类加载器(Class Loader):负责将类的字节码文件加载到内存中,并生成对应的 Class 对象。

  2. 运行时数据区(Runtime Data Area):包括方法区、堆、栈、程序计数器和本地方法栈等。这些区域用于不同类型的数据存储和操作,比如堆用于存储对象实例,栈用于存储局部变量和方法调用。

  3. 执行引擎(Execution Engine):负责执行编译后的字节码文件。它可以通过解释器执行字节码,也可以通过即时编译器将字节码编译成本地机器代码执行。

  4. 本地方法接口(Native Interface):JVM 提供了与本地方法库进行交互的接口,允许 Java 调用本地方法或者本地方法库调用 Java 方法。

  5. 本地方法栈(Native Method Stack):用于执行本地方法,与 Java 虚拟机栈相对应。

加载字节码流程

  • 加载(Loading):通过类加载器(Class Loader)将字节码文件加载到内存中。类加载器负责在类路径下查找并加载字节码文件,然后生成对应的 Class 对象。

  • 验证(Verification):对加载的字节码文件进行验证,确保其格式符合 JVM 规范,不会危害虚拟机的安全。

  • 准备(Preparation):为类的静态变量分配内存空间,并设置默认初始值。

  • 解析(Resolution):将类、接口、字段和方法的符号引用解析为直接引用,这个过程可以在运行期间延迟到真正需要的时候进行。

JVM 加载字节码文件的流程包括类加载器、运行时的数据区域、执行引擎、本地接口和本地方法栈等组成部分,它们在以下步骤中发挥作用:

  1. 类加载器(Class Loader)

    • 类加载器负责将字节码文件加载到内存中。它首先根据类的全限定名找到对应的字节码文件,并将其读取到内存中。
    • 类加载器还会对加载的字节码文件进行验证,确保其格式符合 JVM 规范,不会危害虚拟机的安全。
  2. 运行时数据区域(Runtime Data Area)

    • 方法区:用于存储类的结构信息,包括类的字段、方法、常量池等。类加载器在加载字节码文件时,会将这些信息存储在方法区中。
    • 堆:用于存储对象实例。当类加载器加载字节码文件后,会在堆中为类的实例分配内存空间。
    • 栈:用于存储局部变量和方法调用。当执行引擎执行字节码时,会使用栈来保存方法的局部变量以及方法调用的信息。
    • 程序计数器:用于线程之间切换和记录方法执行的位置。程序计数器指向当前正在执行的字节码指令。
  3. 执行引擎(Execution Engine)

    • 执行引擎负责执行字节码文件中的指令。它可以通过解释器执行字节码,逐条解释并执行指令。
    • 对于经过热点代码的方法,执行引擎还可以使用即时编译器将字节码编译为本地机器代码,以提高执行效率。
  4. 本地方法接口(Native Interface)

    • 如果字节码文件中包含调用本地方法的指令,JVM 将使用本地方法接口与本地方法库进行交互。
    • 本地方法接口允许 Java 调用本地方法或者本地方法库调用 Java 方法,实现了 Java 与底层系统的交互。
  5. 本地方法栈(Native Method Stack)

    • 当执行本地方法时,JVM 会使用本地方法栈来执行本地方法。本地方法栈与 Java 虚拟机栈相对应,用于执行本地方法所需的操作。

在整个加载字节码文件的流程中,类加载器负责加载和验证字节码文件,运行时数据区域存储了加载的类信息和对象实例,执行引擎执行字节码指令,本地接口实现 Java 与本地方法的交互,本地方法栈用于执行本地方法。这些组成部分相互协作,使得 JVM 能够正确加载和执行字节码文件。

运行时数据区-总览

  • 线程不共享(独享)
    • 程序计数器
    • Java虚拟机栈
    • 本地方法栈
  • 线程共享
    • 方法区

接下来我们对内存区域进行剖析

在这里插入图片描述

1. 程序计数器

  • 虚拟机中的一块内存空间,它是线程私有的,也即每个线程都有自己独立的程序计数器。程序计数器可以看作是当前线程所执行的字节码的行号指示器,即它指向当前线程正在执行的字节码指令的地址。在Java虚拟机规范中,程序计数器是唯一一块在Java虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

  • 在程序计数器中,存储着当前线程正在执行的 Java 字节码指令的地址,或者正在执行的本地方法的指令地址。由于程序计数器是线程私有的,因此它不会发生线程切换时的数据同步问题。

  • 程序计数器在线程切换、线程恢复以及指令重复执行等方面发挥着重要作用。在多线程环境下,程序计数器能够确保线程在切换后能够正确恢复到之前的执行位置,从而保证了程序的正常执行。

与操作系统的一些区别:

  • 操作系统程序计数器(OS 程序计数器):在操作系统中,程序计数器是处理器中的一个寄存器,用于存储当前正在执行的指令的地址或者下一条即将执行的指令的地址。它是处理器的一部分,用于支持指令级的控制流。
  • Java 虚拟机程序计数器(JVM 程序计数器):在 Java 虚拟机中,程序计数器是线程私有的内存区域,用于存储当前线程正在执行的字节码指令的地址。

2. 虚拟机栈

用于存储线程的方法调用和局部变量。每个线程在创建时都会被分配一个对应的虚拟机栈,用于跟踪线程的方法调用和执行情况。

开始分析前,还需要了解一些概念

  • 栈帧:每个方法在被调用时都会创建一个栈帧(Stack Frame),栈帧包含了方法的局部变量表、操作数栈、动态链接、返回地址等信息。
  • 栈深度限制:虚拟机栈对方法的调用深度有一定的限制,当方法调用层次过深时,会抛出 StackOverflowError 异常。
  • 栈内存大小:虚拟机栈的内存大小可以通过启动参数进行调整
    • 这也是重要的调优手段之一

栈帧

我们再对栈帧具体探索:
栈帧(Stack Frame)是虚拟机栈中的一个重要概念,它包含了方法在执行过程中所需的各种信息。以下是栈帧通常包含的信息:

  1. 局部变量表(Local Variable Table):用于存储方法中的局部变量,包括方法参数、方法内部定义的变量等。局部变量表中的每个元素都可以存储一个基本数据类型或者一个对对象的引用。

  2. 操作数栈(Operand Stack):操作数栈用于存储方法执行过程中的操作数,例如进行算术运算时需要使用的数值。方法执行时会从局部变量表中获取数据,进行计算后将结果存入操作数栈中。

  3. 动态链接(Dynamic Linking):指向运行时常量池中该方法的引用,通过动态链接可以在运行时解析调用的方法、字段等。

  4. 返回地址(Return Address):记录了方法调用结束后需要返回的指令地址,用于恢复到方法调用点继续执行。

  5. 附加信息:栈帧中还可能包含一些额外的附加信息,例如异常处理相关的信息、调试信息等。

栈的运行原理

  • 方法调用: 当一个方法被调用时,一个新的栈帧被压入栈顶。这个栈帧包含了方法的参数、局部变量以及用于存储中间计算结果的操作数栈。

  • 栈的压栈和弹栈: 方法调用时,栈帧被压入栈顶;方法返回时,栈帧被弹出。这种后进先出(LIFO)的结构保证了方法的调用和返回的顺序。

  • 局部变量表: 每个栈帧中包含一个局部变量表,用于存储方法中的局部变量。包括方法参数、方法内部定义的局部变量等。

    • 栈帧中的局部变量表是一个数组,数组中每一个位置称之为(slot) ,long和double类型占用两个槽,其他类型占用一个槽

当一个方法被执行时,Java 虚拟机会在虚拟机栈中创建一个对应的栈帧(Stack Frame)来存储该方法的局部变量和部分运行时数据。让我们通过一个简单的示例来说明虚拟机栈的作用:

假设有以下的 Java 代码:

public class StackExample {
    public static void main(String[] args) {
        int result = addNumbers(3, 5);
        System.out.println("Result: " + result);
    }

    public static int addNumbers(int a, int b) {
        int sum = a + b;
        return sum;
    }
}

当程序执行到 addNumbers(3, 5) 方法调用时,会发生以下操作:

  1. JVM 虚拟机栈为 addNumbers 方法的执行创建一个新的栈帧,用于存储该方法的局部变量和运行时数据。

  2. 在栈帧中,会分配空间用于存储 ab 两个参数,在这个例子中它们分别是3和5。

  3. 方法执行过程中,sum 变量的值也会被存储在该栈帧中。

  4. addNumbers 方法执行结束后,对应的栈帧会被弹出,栈的状态回到调用该方法的地方,同时将 sum 的值作为返回值传递给调用方。

因此我们获得两个局部变量表,一个属于main一个属于addNumbers方法

main方法:
局部变量表:

args: 参数,这里是String数组,但在main方法中未使用。
result: 存储addNumbers方法返回的结果。
slot的使用:

args占用一个slot。
result占用一个slot。
执行过程:

addNumbers(3, 5) 方法调用时,传递参数35,result将存储addNumbers的返回值。
addNumbers方法:
局部变量表:

a: 参数,存储调用时传递的第一个参数。
b: 参数,存储调用时传递的第二个参数。
sum: 存储a和b的和。
slot的使用:

a占用一个slot。
b占用一个slot。
sum占用一个slot。
执行过程:

int sum = a + b; 执行时,将a和b相加的结果存储在sum中。
return sum; 返回sum的值。

这个过程中,虚拟机栈起到了存储方法调用信息、局部变量和返回值的作用,每个方法的执行都会在虚拟机栈中留下相应的痕迹,保证方法的调用和执行能够顺利进行。

3. 本地方法栈

  • Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧
    • native常用C、C++、汇编来实现,提供了直接访问内存和硬件的能力,在多线程的实现中,大量使用了native方法协助
  • 在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来。

4. 堆内存(Java Heap

(听着就很大啊)
Java 堆是 Java 虚拟机管理的内存中最大的一块,被所有线程共享。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数据都在这里分配内存。

虚拟机对堆 的划分

Java虚拟机对堆的划分通常包括年轻代(Young Generation)、老年代(Old Generation)和永久代/元空间(Permanent Generation/Metaspace)等不同的区域。

1. 年轻代(Young Generation):

  • 作用: 年轻代是新创建的对象的主要存放区域,大部分的对象都是朝生夕死的,因此这部分区域的垃圾回收频繁
  • 划分: 年轻代通常被划分为三个部分:Eden区(新生代对象的初始分配区域)和两个Survivor区(S0和S1,用于存放从Eden区复制过来的存活对象)。
  • 对象的生成和晋升: 新创建的对象首先被分配到Eden区,经过垃圾收集后,存活的对象会被移到Survivor区,多次存活的对象最终会晋升到老年代。

2. 老年代(Old Generation):

  • 作用: 老年代用于存放长期存活的对象,

    通常是由年轻代中存活时间较长的对象晋升而来。

  • 对象的晋升: 经过多次年轻代的垃圾收集后,仍然存活的对象会被晋升到老年代。

3. 永久代/元空间(Permanent Generation/Metaspace):

  • 作用: 用于存放类的元信息、静态变量、常量等,不同虚拟机实现中可能存在差异。
  • 替代关系: 在Java 8 及之后的版本,**永久代被元空间(Metaspace)**所取代。Metaspace不再位于堆内,而是位于本地内存,因此不再受到堆大小的限制。

注意事项:

  • 内存管理: 堆内存的管理主要由垃圾收集器负责,不同的垃圾收集器有不同的算法和策略。
  • 配置调优: 开发人员可以通过JVM的启动参数来调整堆的大小,例如使用 -Xmx-Xms 来设置最大堆大小和初始堆大小
  • OutOfMemoryError: 如果堆内存不足,可能会导致OutOfMemoryError异常,开发人员需要通过调整堆大小或优化程序来解决这类问题。

对象在堆中的生命周期

`对象在Java堆中的生命周期通常经历以下阶段:

1. 创建阶段(Creation):

  • 对象通过 new 关键字在堆上进行分配空间,此时对象进入了堆中。
MyObject obj = new MyObject();

2. 引用阶段(Reference):

  • 对象被引用,可以通过引用变量访问到对象。
    关于引用是什么:
    https://blog.csdn.net/m0_51663233/article/details/133755553
MyObject anotherObj = obj;

3. 使用阶段(Usage):

  • 对象被程序使用,成为程序逻辑的一部分,进行各种操作。
int result = obj.calculateResult();

4. 不可达阶段(Unreachable):

  • 对象不再被任何引用变量所引用,成为不可达对象。
  • Java垃圾收集器通过标记-清除、标记-整理等算法,识别并清理不可达对象。
obj = null; // 不再引用原对象

5. 垃圾收集阶段(Garbage Collection):

  • 垃圾收集器在堆中标记并清理不可达对象。
  • 清理后,堆中的空间被释放,用于存放新的对象。

注意事项:

  • 引用关系: 对象的生命周期与其引用关系密切相关。只有当对象不再被引用时,它才能成为垃圾收集的目标。
  • 垃圾收集策略: Java虚拟机的垃圾收集器采用不同的策略来管理堆内存,如分代垃圾收集等。
  • 内存泄漏: 如果对象在不再使用时没有被正确释放,可能导致内存泄漏问题,即堆中的空间不断被占用而无法回收。

对象的生命周期管理主要由Java虚拟机的垃圾收集器负责,确保不再被引用的对象能够被及时释放,从而保持堆内存的有效利用。

5. 方法区(Method Area

方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。

主要存储

在Java 8及之前版本,方法区通常包括永久代(PermGen);而在Java 8之后,永久代被元空间(Metaspace)所取代。
可以发现,方法区都是存的一些持久化的东修
以下是Java方法区的主要元素:

  1. 类的元数据信息: 包括类的结构、方法、字段、接口等信息。

  2. 常量池(Constant Pool): 存储编译期生成的各种字面量和符号引用。

  3. 静态变量(Static Variables): 存储类级别的静态变量。

  4. 运行时常量池: 是常量池的一部分,包含在类加载后进入方法区的。

  5. 即时编译器编译后的代码: 存储已被即时编译器(如HotSpot的C1和C2编译器)编译后的本地机器代码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

变长子网划分问题的二叉树解法

计网的变长子网划分、计组的变长操作码划分、数据结构的哈夫曼编码,都是前缀编码的本质(变长操作码的二叉树解法我还在琢磨中) 【二叉树解法】每条从叶结点到根节点的路径上有且只有一个被分配的结点: 【例】现将一个IP网络划分成…

第七部分:Maven(项目管理工具)

目录 Maven简介 7.1:为什么学习Maven? 7.1.1、Maven是一个依赖管理工具 7.1.2:Maven是一个构建工具 7.1.3:结论 7.2:Maven介绍 7.3:Maven的优点 Maven安装和配置 7.4:安装教程及环境配置 …

阿里在职5年,聊聊测试工程师如何进阶(自动化、性能、测开)

功能测试(所谓“点点点”)在行业中基本能拿到15k左右的薪水,但是你不可能一直点。入行3年后,你需要拥有不止点点点的技能,否则出去面试,你会就会感受到竞争者给你带来的压力,你需要拿出更高级的…

【数据结构(二)】稀疏 sparsearray 数组(1)

文章目录 1. 稀疏数组的应用场景1.1. 一个实际的需求1.2. 基本介绍 2. 稀疏数组转换的思路分析3. 稀疏数组的代码实现3.1. 二维数组转稀疏数组3.2. 稀疏数组转二维数组 4. 课后练习 1. 稀疏数组的应用场景 1.1. 一个实际的需求 问题:     编写的五子棋程序中&…

LangGPT作者教你编写高质量提示词

CoT和ToT能够提升表现,但是会使得模型的使用变复杂。在对话的场景下容易消耗人的耐心;实际应用的场景下,比较消耗人的token。 还有一点需要说明的是,我们在写自己的prompt的时候,不应该盲目地追求和堆砌提示词技巧&am…

Linux快速下载Google Drive数据集

前言 我们做实验的时候,经常需要下各种各样的数据集,但是这些数据集往往都在Google Drive上,这需要科学上网才能访问。同时,就算在自己电脑上能够访问,但是数据集往往是要下在实验室的服务器上的,而通常这…

大数据Doris(二十五):数据导入演示和其他导入案例

文章目录 数据导入演示和其他导入案例 一、数据导入演示

flink的window和windowAll的区别

背景 在flink的窗口函数运用中,window和windowAll方法总是会引起混淆,特别是结合上GlobalWindow的组合时,更是如此,本文就来梳理下他们的区别和常见用法 window和windowAll的区别 window是KeyStream数据流的方法,其…

python使用selenium webDriver时 报错

可能原因和解决: 1. python 解释器 ----> 设置 2. 浏览器版本 与 浏览器驱动版本不一致 ----> 安装同一版本的 (下载chromedriver | 谷歌驱动更高版本的测试版) 参考:Python使用Selenium WebDriver的入门介绍及安装教程-CSDN博客 Selenium安…

【入门篇】1.2 Redis 客户端之 Jedis 详解和示例

文章目录 1. 简介2. Jedis的依赖下载Jedis导入Jedis jar包配置Redis服务器的地址和端口 3. Jedis 的基本操作连接 Redis 服务器设置和获取字符串类型的键值对判断键是否存在删除键设置键的过期时间 4. Jedis 的数据类型操作字符串类型列表类型集合类型哈希类型有序集合类型 5. …

腾讯云优惠服务器有哪些?腾讯云值得买的优惠服务器推荐

互联网世界中,每个人都是主角。而要想在这个世界中玩得更精彩,一个稳定可靠的服务器就显得尤为重要了。今天,我们就来聊聊广受欢迎的腾讯云优惠服务器吧! 首先,“轻量级”服务器是首选。对于一些小型网站、Web应用程序…

11月最新版付费进群源码自动定位+开源

感觉这个和前几天发布的付费进群差不多。 但有部分地方不一样,也是有什么分销分站后台,看见就头大。 没测试具体功能,可以搭建出来,D盾也未检测到加密文件 更多源码请到www.baicxx.com

接口自动化测试实战:JMeter+Ant+Jenkins+钉钉机器人群通知完美结合

前言 一、本地JAVA环境安装配置,安装JAVA8和JAVA17 二、安装和配置Jmeter 三、安装和配置ant 四、jmeter + ant配置 五、jenkins安装和配置持续构建项目 六、jenkins配置流程 前言 搭建jmeter+ant+jenkins环境有些前提条件,那就是要先配置好java环境,本地java环境…

Linux grep 命令

Linux grep 命令 1: 作用 ​ grep是一种文本搜索工具,它能使用特定的搜索模式,包括[正则表达式]搜索文本,并默认输出匹配行。 ​ windows类似的命令是findstr. 2:语法 grep -options(参数)…

2023年北京市安全员-A证证模拟考试题库及北京市安全员-A证理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2023年北京市安全员-A证证模拟考试题库及北京市安全员-A证理论考试试题是由安全生产模拟考试一点通提供,北京市安全员-A证证模拟考试题库是根据北京市安全员-A证最新版教材,北京市安全员-A证大…

PyCharm中常用插件推荐

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

手撕【双向链表】带头双向循环(2)

目录 Test.c DList.h DList.c SLInsert SLErase DList.c总代码 顺序表和链表的对比 今天继续再双向循环链表的基础上做修改。 ❓提问:请你在10分钟内写一个带头双向循环链表。 其实我们只要把SLInsert 和 SLErase 写好就大功告成了!&#x1f197…

Salmon-超快速、准确的基因丰度计算

文章目录 Salmon简介文章引用及适用物种获取转录组并建立索引比较salmon和coverm定量差异 Salmon简介 Salmon 是一款速度极快的程序,能从 RNA-seq 数据中生成高精度的转录本水平的量化估计值。Salmon 通过一系列不同的创新实现了其准确性和速度,包括使用…

预发部署时机器总是重启两次的“简单”排查

作者:曲池 一、问题 前天同学反馈, 搜索业务的核心应用 magellan 在预发环境部署时总是重启两次,刚部署好,开始联调,突然又重启了,也导致老是被人抱怨搜索环境不稳定。 第一反应是,大概率是应用…

下载huggingface预训练模型到本地并调用

写在前面 在大模型横行的时代,无法在服务器上连接外网的研究僧真的是太苦逼了,每次想尝试类似于CLIP,BLIP之类的大模型都会得到“requests.exceptions.ConnectionError: (MaxRetryError("HTTPSConnectionPool(host‘huggingface.co’, …