Java内存区域速览

news2025/1/9 16:40:54

文章目录

  • 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编译器)编译后的本地机器代码。

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

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

相关文章

GPT-4V新玩法登顶GitHub热榜,随手一画就能生成网页!web开发者:感受到了威胁

西风 发自 凹非寺 量子位 | 公众号 QbitAI 随手一画就能生成网页!GPT-4V新玩法登顶GitHub热榜,狂揽3000🌟: 现在只要简单画一画,框一框,点击执行: “啪”地一下,一个带有各种“按钮…

设计模式-状态模式-笔记

状态模式State 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一问题提供了一种解决方案。 经典模式:State、Memento 动机&#xff08…

R语言绘制精美图形 | 火山图 | 学习笔记

一边学习,一边总结,一边分享! 教程图形 前言 最近的事情较多,教程更新实在是跟不上,主要原因是自己没有太多时间来学习和整理相关的内容。一般在下半年基本都是非常忙,所有一个人的精力和时间有限&#x…

modbusRTU通信简单实现(使用NModbus4通信库)

本文实现ModbusRTU通信,使用的是NModbus4通信库,使用 Modbus Slave是一个模拟Modbus协议从机的上位机软件,主要用于模拟测试跟其他主机设备通信的过程。与之成套存在的另一个软件--Modbus Poll,则是模拟Modbus协议主机的上位机软件…

基于ssm+vue交通事故档案系统

摘要 摘要是对文章、论文或其他文本的主要观点、结论和关键信息的简洁概括。由于你没有提供具体的文章或主题,我将为你创建一个通用的摘要。 本文介绍了一种基于SSM(Spring Spring MVC MyBatis)和Vue.js的交通事故档案管理系统的设计与实现…

Unity Text文本首行缩进两个字符的方法

Text文本首行缩进两个字符的方法比较简单。通过代码把"\u3000\u3000"加到文本字符串前面即可。 参考如下代码: TMPtext1.text "\u3000\u3000" "这是一段有首行缩进的文本内容。\n这是第二行"; 运行效果如下图所示: 虽…

Linux(3):Linux 的文件权限与目录配置

把具有相同的账户放入到一个组里面,这个组就是这两个账户的 群组 。在访问资源(操作系统中计算机的资源)时,可以让这个组里面的所有用户都具有访问权限。 每个账号都可以有多个群组的支持。 在我们Liux 系统当中,默认的…

ROS话题(Topic)通信:自定义msg - 例程与讲解

在 ROS 通信协议中,数据是以约定好的结构传输的,即数据类型,比如Topic使用的msg,Service使用的srv,ROS 中的 std_msgs 封装了一些原生的数据类型,比如:Bool、Char、Float32、Int64、String等&am…

为什么Go是后端开发的未来

近年来,Go 编程语言的流行度迅速增加。Go 最初由 Google 开发,迅速成为后端开发中最受欢迎的语言之一,特别是在分布式系统和微服务的开发中。本文将讨论为什么 Go 是后端开发的未来。 Go 简介 Go,又称为 Golang,是由…

7、使用真机调试鸿蒙项目

此处以华为手机为例,版本为鸿蒙4.0. 一、打开手机调试功能 1、打开开发者模式 打开“设置”—“关于手机”,连续点击“软件版本”可打开开发者模式 2、开启USB调试功能 打开“设置”—“系统更新”—“开发者选项”,下拉找到“USB调试”…

1-2 暴力破解-模拟

模拟:根据题目要求编写代码 可分为:图形排版(根据某种规则输出特定图形)、日期问题、其他模拟 一.图形排版 1.输出梯形(清华大学) 法一:等差数列 分析:每行的星号个数为等差数列2n2…

【Java 进阶篇】JQuery 遍历 —— 无尽可能性的 `each` 之旅

在前端的征途中,操作元素是开发者不可避免的任务之一。而在 JQuery 中,each 方法则是处理这个任务的得力助手。本文将深入探讨 each 方法的奇妙之处,以及它与原生的 for...of 循环的关系,带你领略无尽可能性的遍历之旅。 起步&am…

9款AI让你在2分钟内创建任何东西

1、免费AI绘画:LeonardoAi一个免费的 Midjourney 替代品,能够快速创建高品质和风格统一的视觉图片,帮你释放创造力。 2、 模板编辑AI:Canva 将所有AI的强大功能汇聚于一处,为你的工作流程注入超级动力。 3、构建网站&…

基于51单片机步进电机节拍步数正反转LCD1602显示( proteus仿真+程序+原理图+设计报告+讲解视频)

基于51单片机步进电机节拍步数正反转LCD1602显示 📑1. 主要功能:📑2. 讲解视频:📑3. 仿真📑4. 程序代码📑5. 设计报告📑6. 设计资料内容清单&&下载链接📑[资料下…

SpringCloud FeignClient声明式服务调用采坑记录(A调用服务B/C,B/C重启后必须重启A后才能成功调用配置项)

SpringCloud FeignClient声明式服务调用(A调用服务B/C,B/C重启后必须重启A后才能成功调用配置项采坑记录) 1. 报错(info级别的警告信息)2. 原因:使用了默认了cache负载均衡,或者禁用了ribbonLoa…

数据结构02附录01:顺序表考研习题[C++]

图源:文心一言 考研笔记整理~🥝🥝 之前的博文链接在此:数据结构02:线性表[顺序表链表]_线性链表-CSDN博客~🥝🥝 本篇作为线性表的代码补充,每道题提供了优解和暴力解算法&#xf…

基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码

基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于蝠鲼觅食优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…

YOLOv5项目实战(4)— 简单三步,教你按比例划分数据集

前言:Hello大家好,我是小哥谈。本节课就教大家如何去按照比例去划分数据集,希望大家学习之后可以有所收获!~🌈 前期回顾: YOLOv5项目实战(1)— 如何去训练模型 YOLOv5项目

【C#】类型转换-显式转换:括号强转、Parse法、Convert法、其他类型转string

目录 一、括号强转 1.有符号整型 2.无符号整型 3.浮点之间 4.无符号和有符号 5.浮点和整型 6.char和数值类型 7.bool和string是不能够通过 括号强转的 二、Parse法 1.有符号 2.无符号 3.浮点型 4.特殊类型 三、Convert法 1.转字符串 2.转浮点型 3.特殊类型转换…

基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码

基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于黄金正弦算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于黄金正弦优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…