JVM内存结构及程序执行的内存分析过程

news2025/1/5 8:58:50

一. JVM内存结构 

1. JVM的内存结构大概分为 

在这里插入图片描述

堆(Heap)

线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。

方法区(Method Area)

线程共享。存储类信息、常量、静态变量、即时编译器编译后的代码。
方法区(Method Area)与Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java 堆区分开来。

方法栈(JVM Stack)

线程私有。存储局部变量表、操作栈、动态链接、方法出口,对象指针。
每个线程会有一个私有的栈。每个线程中方法的调用又会在本栈中创建一个栈帧。在方法栈中会存放编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象本身。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小 

本地方法栈(Native Method Stack)

线程私有。为虚拟机使用到的Native 方法服务。如Java使用c或者c++编写的接口服务时,代码在此区运行。本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。

程序计数器(Program Counter Register)

线程私有。有些文章也翻译成PC寄存器(PC Register),同一个东西。它可以看作是当前线程所执行的字节码的行号指示器。指向下一条要执行的指令。
可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型中字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖于此计数器。JVM中的程序计数器也是在Java虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。在任意时刻一条JVM线程只能执行一个方法的代码,方法可以是Java方法,或者是native方法。

重点

重点 在Java内存运行时的各个部分中,程序计数器、虚拟机栈、本地方法栈这三个区域随线程生随线程死。栈中的栈帧随着方法的进入和退出有条不紊的进行着出入栈操作。这几个区域是不需要过多的考虑内存回收的问题,因为方法或者线程结束,内存自然就跟着被回收。 然而,Java堆和方法区则不同——一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存可能也不一样。这部分内存的分配是动态的,我们只有在程序运行期间才能知道会创建哪些对象。这部分内存,就是我们关注的重点。

2.  jvm主内存与工作内存

主内存主要包括本地方法区和堆。每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)。

  • 所有的变量都存储在主内存中(虚拟机内存的一部分),对于所有线程都是共享的。
  • 每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。
  • 线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。

java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改。总结java的内存模型,要解决两个主要的问题:可见性和有序性。
多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。当线程操作某个对象时,执行顺序如下:

  • 从主存复制变量到当前工作内存 (read and load)
  • 执行代码,改变共享变量值 (use and assign)
  • 用工作内存数据刷新主存相关内容 (store and write)

2.1 volatile关键字

任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。

2.2 synchronized关键字

java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。
一个线程执行临界区代码过程如下:

  1. 获得同步锁
  2. 清空工作内存
  3. 从主存拷贝变量副本到工作内存
  4. 对这些变量计算
  5. 将变量从工作内存写回到主存
  6. 释放锁

可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
 

 二. 程序执行的内存分析过程

示例代码:

public class Person {

       String name;

       int age;

       public void show(){

              System.out.println("姓名:"+name+",年龄:"+age);

       }

}

public class TestPerson {

       public static void main(String[ ] args) {

              // 创建p1对象

              Person p1 = new Person();

              p1.age = 24;

              p1.name = "张三";

              p1.show();

              // 创建p2对象

              Person p2 = new Person();

              p2.age = 35;

              p2.name = "李四";

              p2.show();

       }

}

对上面代码执行过程的内存分析(借助上图理解):

1、从main()方法开始设置栈帧,因为main方法是程序的入口,args的值为null

2、创建p1对象,p1变量的值为null,因其是引用类型的

3、new Person()执行构造方法,虚拟机栈中要新开辟一个栈桢Person()

4、此时堆里面会新增一个对象,对象里name默认值为null,age默认值为0

5、方法区里有Person类,而show()方法的具体信息会存放在Person类中

6、若方法区的show()有地址,则堆中的show指向方法区的show();假设堆中的对象地址为0x1024,因Person p1 = new Person()对象赋值给p1,故p1的地址为0x1024,main()栈帧的p1指向堆中的对象

7、执行完Person p1 = new Person(),Person()栈帧出栈

8、p1.age = 24进行赋值操作,地址里的name不再是null,值变为24

9、p1.name = "张三"进行赋值操作,“张三”并不是基本数据类型,String在java中相当于一个类(class)属于引用数据类型,在方法区的字符串常量池,将该地址给name

10、p1.show()调用会在虚拟机栈中开辟一个为p1.show()的栈帧,该栈帧有默认参数this,地址为0x1024,this对应的是当前调用的方法

11、执行p1.show(),打印姓名、年龄后,p1.show()栈帧出栈

12、同理创建p2对象之后代码的执行过程跟p1一样,重复2-10的步骤

13、最后main栈帧出栈,虚拟机栈回收;堆被垃圾处理器回收,方法区也在内存中被清除
 

 

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

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

相关文章

机器学习-12 卷积神经网络简介

卷积神经网络 引言深度学习发展历程深度应用领域深度学习vs传统机器学习深度神经网络vs浅层神经网络深度学习概述 卷积神经网络CNNBP神经网络CNN概述卷积神经网络大致结构卷积神经网络大致过程 局部连接权值共享非线性映射ReLU(Rectified Linear Units)池…

盘点一下架构师主流的画图工具(附地址)

盘点一下架构师主流的画图工具(附地址) 转发我个人微信公众号的内容,后续优先公众号。 一、文章来源 写这篇文章的目的是做个关于常用画图工具的总结。 起源是在架构组时为了降低沟通成本和提高作战效率,我们频繁用图交流&…

13. 精灵动画Sprite和SpriteSequence的基本使用

1. 说明: 在unity二维游戏开发中,有一种精灵类的玩家角色,通过一系列动作的静态图片可以合成该精灵的某一个动作。在QML当中也有一个控件可以实现这种精灵类动画的制作,主要使用到三个控件:Sprite和SpriteSequence和A…

一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

我们都知道 ,python类中有三种常见的方法 ,分别是实例方法 ,类方法和静态方法 。那么这几个方法到底有什么作用 ? 它们之间有什么区别 ?该如何使用 ? 带着这些问题 ,下面我们就来了解下这三种方…

windows平台python脚本执行环境搭建笔记

1.python脚本环境下载 这里是原始发布源: https://www.python.org/downloads/release/python-3114/https://www.python.org/downloads/release/python-3114/安装时记得添加进系统path,这样你可以随时调用python环境。 2.扩展模块的安装 step1.找到py…

35款优秀的 SpringBoot/SpringCloud 开源项目,开发脚手架,总有一款适合你...

简介 SpringBoot 是一个非常流行的 Java 框架,它可以帮助开发者快速构建应用程序。他不仅继承了 Spring 框架原有的优秀特性,而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。 最近,小编蹲点各大开源网站、社区等&#x…

iOS App的打包和上架流程

转载:iOS App的打包和上架流程 - 掘金 1. 创建账号 苹果开发者账号几种开发者账号类型 个人开发者账号 费用:99 美元/年(688.00元)协作人数:仅限开发者自己不需要填写公司的邓百氏编码( D-U-N-S Number…

源码分析spring容器启动销毁资源

文章目录 一、InitializingBean二、SmartInitializingSingleton三、PostConstruct四、DisposableBean五、PreDestroy六、BeanPostProcessor七、ApplicationContextAware八、Bean初始化销毁过程 spring项目启动时,在bean的生命周期内,可以添加一些前置、后…

ATE测试工程师的前景怎么样?能转DFT工程师吗?

最近后台不少同学私信想要咨询ATE这个岗位,想了解这个岗位的薪资,前景,以及相关的技能,下面就来一起了解一下~ 什么是ATE? ATE是(Automatic Test Equipment)的缩写, 于半导体产业意…

OOD 使用基于提示的特征映射生成用于视频异常检测

paper link 本文提出了使用提示引导特征映射的生成式视频异常检测框架,作者来自中山大学,文章发表在cvpr2023 作者首先分析了现有方法并指出当前面临的两个问题 两个关键挑战 大多数视频异常检测方法通过在训练阶段学习正常事件的分布,并在测试阶段检…

别找了!前端那些好用的网站都在这里了!【文末送书】

🍀前言 好用的网站千千万万,如果你还发现好用的网站,欢迎在评论区中留言分享😁,赠书活动在文末哟,中奖者可以从给出的五本书中任意挑选自己喜欢的那本 文章目录 🍀前言 🍀一、渐变…

简化 Hello World:Java 新写法要来了

OpenJDK 的 JEP 445 提案正在努力简化 Java 的入门难度。 这个提案主要是引入 “灵活的 Main 方法和匿名 Main 类” ,希望 Java 的学习过程能更平滑,让学生和初学者能更好地接受 Java 。 提案的作者 Ron Pressler 解释:现在的 Java 语言非常…

快速上手Flutter

目录 一、Flutter介绍 1.高效开发 2.优异的性能 3.较低的开发成本 4.社区活跃 二、Flutter使用 1.Dart 语言 2.什么是Dart语言 3.Flutter 组件库 4.Layout 布局 5.Flutter 工具 6.Flutter社区 三、Flutter使用技巧 四、总结 一、Flutter介绍 Flutter是谷歌的移动…

[CKA]考试之节点维护-指定 node 节点不可用

由于最新的CKA考试改版,不允许存储书签,本博客致力怎么一步步从官网把答案找到,如何修改把题做对,下面开始我们的 CKA之旅 题目为: Task 将k8s-node1节点设置为不可用,然后重新调度该节点上的所有Pod 注…

优维低代码实践:模板

优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。 优维…

如何提高测试用例的编写效率?

1、提高测试覆盖率 我们通过对测试用例的评审,进一步完善测试覆盖率。在评审过程中,不同的评审专家看待问题的角度不完全一致,因此我们需要充分考虑测试方法,扩充测试用例的全面性,确保基本功能和核心功能的覆盖率。 如…

操作系统课后题答案(费翔林)

仅为老师布置的课后题答案,仅供学习参考 习题2

数据的存储方式(Parquet、ORC)

文章目录 数据的存储方式按行存储按列存储 Parquest文件布局概念并行处理的单元 配置Row Group Size 行组的大小Data Page Size 数据页的大小 元数据数据页Hive下的Parquet实验Parquet简单工具的使用支持的组件 Apache ORC文件布局Stripe Hive下的Parquet实验ORC简单工具的使用…

NLP——part of speech (POS)中的隐马尔可夫模型 + Viterbi 算法

文章目录 POS隐马尔可夫模型计算简介转移概率矩阵(Transition matrix)观察矩阵(Observation / emission Matrix)预测 predictionVitervi 算法练习 POS 词性标注(Part-of-Speech Tagging,POS Tagging&#…

【AUTOSAR-Code调试】:Wdog

【AUTOSAR-Code调试】:Wdog DavinciCfg 生成文件添加进GreenHill编译添加.c文件添加.h文件路径 接上篇【Davinci开发】:Wdg配置 DavinciCfg 生成文件添加进GreenHill编译 添加.c文件 添加.h文件路径