JVM专题六:JVM的内存模型

前面我们通过Java是如何编译、JVM的类加载机制、JVM类加载器与双亲委派机制等内容了解到了如何从我们编写的一个.Java 文件最终加载到JVM里的,今天我们就来剖析一下这个Java的‘中介平台’JVM里面到底长成啥样。

JVM的内存区域划分

Java虚拟机(JVM)是Java程序运行的虚拟计算机,它负责将Java字节码转换为特定平台上的机器指令。JVM的内存区域划分是JVM规范中定义的,它规定了JVM在执行Java程序时所管理的不同内存区域。JVM内存区域的主要划分如下图所示:

方法区&&元空间

        用于存储类信息、常量、静态变量等数据的内存区域。它是所有线程共享的,用于支持类和接口的组织。在HotSpot JVM中,它通常被实现为永久代或元空间。从Java 8开始替代了永久代,用于存储类的元数据,如类的静态结构。元空间不是JVM堆的一部分,而是使用本地内存,有助于避免内存溢出错误。

Java堆内存

        堆是用于存储对象实例和数组的内存区域,是垃圾回收器管理的主要区域,也是所有线程共享的。堆通常分为新生代和老年代,垃圾收集器定期清理无用对象以回收内存。

线程栈内存

         栈是每个线程独有的内存区域,用于存储局部变量、操作栈和方法调用信息。栈由栈帧组成,每个栈帧包含局部变量表、操作数栈、动态链接信息和方法返回地址。

本地方法栈

        本地方法栈用于支持JVM使用本地方法时的内存管理,类似于Java栈,但是用于管理本地方法调用。

程序计数器

        程序计数器是每个线程都有一个的内存区域,用于记录当前线程执行的字节码的行号指示器。线程切换时,程序计数器也会切换到下一个方法的起始点。

直接内存

        直接内存虽然不是JVM运行时数据区的一部分,但是Java NIO允许使用直接内存进行高效的I/O操作。直接内存不是由JVM管理,但是可以通过Java代码进行分配和释放。

        每个内存区域都有其特定的用途和GC行为,了解这些区域可以更好地理解Java程序的内存管理和性能调优,为后续我们谈论GC相关做好铺垫。

JVM内存模型实例

        老样子,上一章节我们从理论介绍来JVM内存区域划分及其各个区域应该存放的数据下面还是通过一段简单的代码来一起探究这个过程中数据是怎样流转的?

public class App {public static void main(String[] args) {SpringApplication sApp = new SpringApplication();// sApp.run(App.class, args);}}

1、存放类的方法区&&元空间

其实在jdk1.8以后改成元空间更加利于我们的理解了,元空间存放元数据的空间,对于Java虚拟机谁是他的元数据呢?当然是class相关的数据呢,所以元空间(方法区当然是存放类相关数据的呢)。上述代码在JVM中的大概位置如图:

2、程序计数器

通过前面Java编译大家可以明白:我们编写好的源代码,会被编译成各种字节码指令,然后字节码指令会被一条一条的执行。所以JVM在加载class文件后需要有个可以执行字节码指令的工具,在JVM中这个工具就是字节码执行引擎。

但是字节码执行引擎只是用来执行指令的,具体执行到哪里了就需要另外程序计数器来记录。如下图所示:

Java虚拟机(JVM)是支持多线程的,它允许多个线程并发执行。在Java中,每个线程都有自己的程序计数器(Program Counter,PC),这个计数器是线程私有的。程序计数器用于存储当前线程正在执行的字节码指令的地址,确保线程在执行过程中能够正确地跟踪执行状态。

因此每一个线程执行字节码时,程序计数器会指向当前正在执行的指令。如果线程被暂停或阻塞,程序计数器会保持在当前指令的位置,这样当线程再次被调度执行时,可以从上次暂停的地方继续执行。

 如下图更加准确描述了他们之间的关系:

在Java中,代码的执行总是由线程来驱动的。即使是简单的main()方法,也是由JVM在启动时创建的名为main的线程来执行的。这个线程是Java程序的入口点,它负责执行main()方法中的代码。当main线程开始执行main()方法时,它的程序计数器会记录当前执行的字节码指令的地址。程序计数器是每个线程私有的内存区域,它的作用是确保线程能够跟踪其执行状态,包括当前执行到哪一行代码或哪一个字节码指令。

3、Java虚拟机栈 

上面介绍的程序计数器是用来记录指令执行的位置的且与每个线程有关,但是我们知道其实除了类变属性以外,其实每个方法都有自己的局部变量如下是的代码示例:

public class App {public static void main(String[] args) {SpringApplication sApp = new SpringApplication();//sApp.run(App.class,args);sApp.getRunListeners(args);}
}public class SpringApplication {public String run(Class appClass, String[] args) {System.out.println("my Spring Application ");return "run args";}public String getRunListeners( String[] args) {System.out.println("My  getRunListeners ");String myRunListenersVar = "myRunListenersVar";this.getSpringFactoriesInstances(args);return "run args";}public String getSpringFactoriesInstances( String[] args) {System.out.println("My  getSpringFactoriesInstances ");String mySpringFactoriesInstancesVar = "mySpringFactoriesInstances";return "run args";}}

上述代码SpringApplication类的getRunListeners方法与getSpringFactoriesInstances方法都有各自的局部变量,因此Java虚拟机也必须有一块内存空间去存该部分数据。

上述代码运行mian方法,会将首相会将App类里的main作为第一个栈帧压倒main线程所在的Java虚拟机栈,如下图所示:

进入SpringApplication#getRunListeners方法时,会将getRunListeners作为第二个栈帧压入main线程所在的Java虚拟机栈,同时在getRunListeners我们声明里局部变量myRunListenersVar变量,该变量在栈帧getRunListeners里。如下图所示:

方法继续向下执行,进入getSpringFactoriesInstances方法,会将getSpringFactoriesInstances压入main线程的Java虚拟机栈顶,如下图所示:

因为Java栈结构是先进后出,后续便会按照getSpringFactoriesInstances -> getRunListeners -> main 顺序进行弹栈,知道main方法运行结束。所以局部变量只会在方法内部生效,同时Java虚拟机栈又是线程内执行,所以后续介绍Java并发编程的时候我们说线程安全的时候也会提到,局部变量避免并发冲突等。

介绍完上述内容,我们再用一张图描述下:

4、Java堆内存

在介绍栈的过程中有意识的跳过了上述代码中关于创建SpringApplication这块的代码,我们再回头看看这段代码。

public class App {public static void main(String[] args) {SpringApplication sApp = new SpringApplication();}
}

上述 new SpringApplication()代码就是创建了一个SpringApplication对象实例,同样作为JVM也需要找个地方来存放对象实例数据,而这个地方被称作为Java堆内存。

当我们创建一个对象的时候,会将这个对象的实例数据放到堆内存中,然后把这个实例存放的引用返回给局部变量,这样我们就持有了对象实例的地址。

还是画一张图更加清晰一点:

5、整体流程

介绍整体流程之前,其实还有个区域就是Java为了区分我们写的方法和自己内置调用的方法,专门用来处理本地方法的调用区域管理,这块JVM称之为本地方法栈。至此整个流程就可以完整画出来了。

1、你的JVM进程会启动,就会先加载App类到内存里。然后有一个main线程,开始执行你的App中的main()方法。main线程是关联了一个程序计数器的,那么他执行到哪一行指令,就会记录在这里
2、main线程在执行main()方法的时候,会在main线程关联的Java虚拟机栈里,压入一个main()方法的栈帧。
3、接着会发现需要创建一个SpringApplication类的实例对象,此时会加载SpringApplication.class文件到内存里来
4、创建一个SpringApplication的对象实例分配在Java堆内存里,并且在main()方法的栈帧里的局部变量表引入一个sApp”变量,让他引用SpringApplication对象在Java堆内存中的地址。
5、main线程开始执行SpringApplication对象中的方法,会依次把自己执行到的方法对应的栈帧压入自己的Java虚拟机栈
6、执行完方法之后再把方法对应的栈帧从Java虚拟机栈里弹出来,

那么JVM中的各个核心内存区域的功能和对应的我们的Java代码之间的关系,就彻底理解

其实到这里JVM相关应该就结束了,但是在上面理论介绍的时候我们还提及到了直接内存,其实这块放到IO相关模块更加合适,上述提及到是为了给大家理解元空间做的实例。

同样最后的最后我们思考一两个问题,上述我们给JVM划分了各个内存区域放各种数据,随着程序的运行或者数据量变多了,内存放不下了怎么办呢?或者在分配内存的时候大家地址重复了又该咋整呢?

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 高级 - 第十二章 | 数据库的设计规范

目录 第十二章 数据库的设计规范12.1 为什么需要数据库设计12.2 范式12.2.1 范式简介12.2.2 范式都包括哪些12.2.3 键和相关属性的概念12.2.4 第一范式(1st NF)12.2.5 第二范式(2nd NF)12.2.6 第三范式(3rd NF&#xf…

【鸿蒙 HarmonyOS】尺寸设置:size/layoutWeight/constraintSize

一、背景 常见尺寸:width(宽度)、height(高度)、padding(内边距)、margin(外边距) 主要整理下size(设置高宽尺寸)、layoutWeight(对…

Linux之旅: 基础知识点的终极指南

文章目录 1、Linux的目录结构2、ls命令3、管理文件和目录4、linux命令使用细节和技巧5、权限管理基本命令6、搜索命令7、管道符与重定向8、压缩和解压命令9、用户及vim编辑器10、用户和用户组管理一、Linux系统用户账号的基本管理二、Linux系统用户组的管理 1、Linux的目录结构…

RedHat9 | Web服务配置与管理(Apache)

一、实验环境 1、Apache服务介绍 Apache服务,也称为Apache HTTP Server,是一个功能强大且广泛使用的Web服务器软件。 起源和背景 Apache起源于NCSA httpd服务器,经过多次修改和发展,逐渐成为世界上最流行的Web服务器软件之一。…

2024年6月大众点评成都餐饮店铺POI分析22万家

2024年6月大众点评成都餐饮店铺POI共有221002家 店铺POI点位示例: 店铺id CACuqlcUQApLA7Ki 店铺名称 峨眉山豆腐脑(百吉街店) 十分制服务评分 7.3 十分制环境评分 7.5 十分制划算评分 7.1 人均价格 18 评价数量 38 店铺地址 百吉街86号1层 大类 美食 中类…

css布局之flex应用

/*父 100*/.parent-div {/* 这里添加你想要的属性 */display: flex;flex-direction: row; //行justify-content: space-between; //左右对齐align-items: center;flex-wrap: wrap; //换行}/*中 90 10 */.middle-div {/* 这里添加你想要的属性 */display: flex;flex-direction:…

idea2022激活

下载激活脚本 解压后,打开文件夹如下:ja-netfilter.jar 为激活补丁: 复制补丁所在的整个文件夹到硬盘某个位置 将 ja-netfilter补丁所在的整个文件夹移动到电脑上某个位置,我是放到了 D 盘下: (路径中不…

Docker配置阿里云加速器(2续)

默认情况下镜像是从docker hub下载,由于docker hub服务器在国外,由于网络原因镜像下载速度较慢,一般会配置镜像加速进行下载 国内镜像加速器有阿里云、网易云、中科大等,本章配置阿里云镜像加速器,速度较快 镜像加速源…

【鸿蒙】 模拟器运⾏

【鸿蒙】HUAWEI DevEco Studio安装-CSDN博客 【鸿蒙】创建第⼀个鸿蒙项⽬-CSDN博客 点击 Tools 菜单下的 Device Manager 点击 Install ,安装模拟器 下载模拟器相关的SDK,点击 Finish 选择安装⽬录,点击 New Emulator 选择设备类型&#…

FlinkCDC sink paimon 暂不支持exactly-once写入,而通过 幂等写

幂等写入: 一个幂等操作无论执行多少次都会返回同样的结果。例如,重复的向hashmap中插入同样的key-value对就是幂等操作,因为头一次插入操作之后所有的插入操作都不会改变这个hashmap,因为hashmap已经包含这个key-value对了。另一…

Linux 文件权限

优质博文:IT-BLOG-CN 一、使用者与群组的概念 【1】在Linux里面,任何一个文件都具有[User,Group及Other]三种身份的个别权限:不过需要注意的是root用户,具有所有权限。 ✔ User(文件拥有者):只有文件拥有者&#xf…

【yolov8语义分割】跑通:下载yolov8+预测图片+预测视频

1、下载yolov8到autodl上 git clone https://github.com/ultralytics/ultralytics 下载到Yolov8文件夹下面 另外:现在yolov8支持像包一样导入,pip install就可以 2、yolov8 语义分割文档 看官方文档:主页 -Ultralytics YOLO 文档 还能切…

Minillama3->sft训练

GitHub - leeguandong/MiniLLaMA3: llama3的迷你版本,包括了数据,tokenizer,pt的全流程llama3的迷你版本,包括了数据,tokenizer,pt的全流程. Contribute to leeguandong/MiniLLaMA3 development by creating an account on GitHub.https://github.com/leeguandong/MiniLL…

Android招聘市场技术要求越来越高,从事三年开发是否应该考虑转行?

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。 不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧! 《Framework精编…

Apple - Cocoa Event Handling Guide

本文翻译整理自:Cocoa Event Handling Guide( https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/Introduction/Introduction.html#//apple_ref/doc/uid/10000060i 文章目录 一、导言本文件的组织另见 二、事件…

[FreeRTOS 基础知识] 互斥访问与回环队列 概念

文章目录 为什么需要互斥访问?使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问? 在裸机中,假设有两个函数(func_A, func_B)都要修改a的值(a),那么将a定义为全局变…

SpringMVC系列二: 请求方式介绍

RequestMapping 💞基本使用💞RequestMapping注解其它使用方式可以修饰类和方法可以指定请求方式可以指定params和headers支持简单表达式支持Ant 风格资源地址配合PathVariable 映射 URL 绑定的占位符注意事项和使用细节课后作业 上一讲, 我们学习的是Spr…

Vue78-缓存路由组件

一、需求 路由切走的时候&#xff0c;组件会被销毁&#xff0c;路由切回来&#xff0c;组件被挂载&#xff01; 需要&#xff1a;路由切走的时候&#xff0c;组件不会被销毁。 二、代码实现 若是不加include属性&#xff0c;则在<router-view>里面展示的路由&#xff0c…

U盘容量变0字节:详解原因、恢复方案与预防措施

一、U盘容量变0字节的现象 在日常工作和学习中&#xff0c;U盘作为一种便携式存储设备&#xff0c;被广泛用于数据交换和存储。然而&#xff0c;有时我们可能会遇到一种奇怪的现象&#xff1a;原本容量正常的U盘&#xff0c;在插入电脑后突然显示容量为0字节。这种情况意味着我…

【因果推断python】51_去偏/正交机器学习3

目录 What is Non-Parametric About? What is Non-Parametric About? 在我们继续之前&#xff0c;我只想强调一个常见的误解。当我们考虑使用非参数 Double-ML 模型来估计 CATE 时&#xff0c;我们似乎会得到一个非线性治疗效果。例如&#xff0c;让我们假设一个非常简单的数…