JVM—内存结构

news/2024/4/15 3:46:18/文章来源:https://blog.csdn.net/m0_66510521/article/details/136442558

首先说一下:C/C++ 和 Java 一个非常主要的区别就是内存的管理,C/C++ 是 手动开辟和释放的,而Java是依赖于JVM来进行管理

注:跟内存模型JMM不是一件事

1. 程序计数器:

  • 理解:是一块很小的内存区域,可以看作是当前线程所执行的字节码的行号指示器。它标识了下一条需要执行的字节码指令,通过它来选取下一条字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要这个计数器来完成。
  • 作用:
    • 字节码解释器通过改变程序计数器来读取指令,从而实现代码的流程控制。
    • 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程切换回来之后能知道该线程上次运行到哪了

注:唯一不会出现oom的内存区域

  • 总结:线程私有的,一个线程的执行就是在该计数器的不断变化推动下一步一步完成的。

2. Java虚拟机栈:

  • 理解:线程私有,除了Native方法之外,所有的Java方法调用通过调用栈来实现的(当然也需要其他运行时数据区进行配合),生命周期和线程一致
  • 工作过程: 每一个方法调用都会有对应的栈帧被压入栈中,抛出异常和return指令都会导致栈帧弹出
  • 栈帧:包含局部变量表、操作数栈、动态链接、方法返回地址
  • 说明:如果请求的栈深度大于虚拟机允许的深度,就会StackOverFlowError异常;如果栈容量可以动态扩容,当栈拓展到无法申请足够的内存会抛出OurOfMemoryError

2.1关于栈帧的介绍★

线程调用一个方法就对应着一个栈帧的入栈和出栈。栈的顶部第一个栈帧叫做当前栈帧,对应的是一个线程需要执行的最新的方法;栈帧内部主要包括局部变量表,操作数栈,方法返回地址,动态链接等。

2.1.1局部变量表

介绍:是一组变量值的存储空间,用于存放方法参数,和方法内部定义的局部变量,主要包括编译器就可知道的各种基本数据类型,对象引用等,所以局部变量表所需要的内存大小编译器就能确定下来,并且在整个方法运行期间都不会改变

变量槽:(了解即可)

  • 局部变量表的容量以变量槽为最小单位
  • Java虚拟机通过索引定位的方式使用局部变量表,索引值的范围从0开始致局部变量表最大的变量槽数量
  • 如果执行的是实例方法,那局部变量表中的第0位索引的变量槽默认是用于传递方法所属对象实例的引用”this“
  • 为了尽可能节省栈帧用的内存空间,局部变量表中的变量槽是可以重用的,如果当前字节码PC计数器的值已经超过某个变量的作用域,那这个变量对应的变量槽就可以交给其他变量来使用

2.1.2操作数栈

介绍:主要用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。当一个方法执行过程中,会有各种字节码指令对各种操作数栈出栈和入栈操作。比如字节码指令iadd,这条指令在运行的时候要求操作数栈中最接近栈顶的两个元素已经存入两个int型的数值,当执行这个指令时,会把两个int型值出栈并相加,然后将相加的结果重新入栈

2.1.3动态链接

每个栈帧都包含一个指向运行时常量池中该“栈帧所属方法”的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。在 Java 源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在 Class 文件的常量池中。比如: 描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用

2.1.4方法返回地址

  • 方法正常退出: 一个方法正常执行完成之后,会遇到返回指令。这种情况会有一个预先定义好类型的返回值返回给调用方法。
  • 方法异常退出:一个方法执行过程中,如果发生了异常,并且异常没有再方法中妥善的捕获处理,那也会触发方法的退出。这种情况是不会有返回值返回给调用方的。

方法正常退出时,主调方法的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中就一般不会保存这部分信息。本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方去的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去

附加信息

栈帧中还允许携带与 Java 虚拟机实现相关的一些附加信息。例如,对程序调试提供支持的信息,但这些信息取决于具体的虚拟机实现

3. 本地方法栈:

  • 理解:为虚拟机使用的Native方法服务,作用原理跟虚拟机栈相似
  • 就是一个Java调用非Java代码的接口

4. 堆 ★

  • 理解:Java虚拟机所管理内存最大的一部分,Java堆时所有线程共享的一块内存区域,在虚拟机启动时创建。是垃圾收集器的主要管理区域
  • 作用:存放对象实例几乎所有的对象实例以及数组都是在这里分配内存
  • 组成:在Java 8和7之间,内存堆的一些重要变化包括以下几点:
    1. 永久代的移除:Java 8取消了永久代(PermGen),取而代之的是元空间(Metaspace)。永久代在Java 7中用于存储类的元数据和常量池等信息,而在Java 8中,这些信息被迁移到了元空间中。
    2. 字符串常量池的变化:在Java 8中,字符串常量池不再位于永久代中,而是被移到堆中。
    3. 元空间的垃圾回收:元空间不再使用传统的垃圾回收机制,而是使用了基于本地内存管理的垃圾回收。这种方式减少了对垃圾回收造成的应用程序停顿时间。
    4. 元空间的存储位置:在Java 7中,永久代位于堆中的一部分,而在Java 8中,元空间与堆是完全分离的,它可以在物理上独立存在。
    5. 元空间的动态分配:在Java 8中,元空间的大小可以动态调整,不再使用预定义的固定大小。这使得元空间可以根据应用程序的需求进行自动扩展和收缩。

总的来说,Java 8对内存堆的变化主要集中在永久代的取消、元空间的引入和动态分配,以及字符串常量池的位置变化等方面,这些变化都旨在提高内存管理的灵活性和性能。

5.方法区:

  • 理解:是一块存储类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据
  • 常量池:一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述信息外,还包含一项信息那就是常量池表(Constant Pool Table),包含各种字面量和对类型、域和方法的符号引用。

一些变化:

  1. Java 7 中,方法区确实位于永久代 (PermGen),但 PermGen 的大小是可变的,可以使用 JVM 参数进行调整;同时,PermGen 中存放的除了方法区外,还包括类的元数据信息、字符串常量池、静态变量等信息。
  2. Java 7 中,确实将部分静态变量的存放位置从 PermGen 移到了堆中,并且可以通过 JVM 参数 -XX:PermSize-XX:MaxPermSize 来调整 PermGen 的大小。
  3. Java 8 中废除了永久代,并将方法区迁移到了元空间 (Metaspace),元空间与堆不相连,但确实与堆共享物理内存。另外,Java 8 中还引入了一个新参数 -XX:MaxMetaspaceSize 用于控制元空间最大可用内存

运行时常量池

运行时常量池(Runtime Constant Pool) 是方法区的一部分。Class 文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是常量池表(ConstantPool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放,JVM 为每个已加载的类型(类或接口)都维护一个运行时常量池,在加载类和接口到虚拟机后创建。所以运行时常量池相对于Class文件常量池的另一重要特性: 具备动态性

既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutofMemoryError异常

6.本地内存和直接内存

  • 本地内存(Native Memory) 并不是虚拟机运行时数据区的一部分,它也不是Java虚拟机规范定义的内存区域。我们可以看到在 HotSpot 中,JDK1.8就将方法区移除了,用元数据区来代替,并且将元数据区从虚拟机运行时数据区移除了,转到了本地内存中,也就是说这块区域是受本机物理内存的限制,当申请的内存超过了本机物理内存,才会抛出 OutofMemoryError 异常。
  • 直接内存(Direct Memory) 也是受本机物理内存的限制,在JDK1.4中新加入的NIO (new input/output) ,引入了一种基于通道 (Channel) 与缓冲区(Buffer)的I/0方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在Java堆里面的 DirectByteBuffer 对象作为这块内存的引用操作,这样避免了在Java堆和Native堆中来回复制数据,显著提高性能
    Java 程序内存 = JVM 内存 + 本地内存
    本地内存 = 元空间 + 直接内存

额外补充

对象的创建过程:new的动作后JVM做的事

  • 类加载检查:先去常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已经被加载过、解析和初始化过。如果没有那必须先器进行类加载
  • 分配内存:类加载之后就能确定对象的大小,为对象分配空间的任务等同于把一块确认大小的内存从堆中划出来,有两种方式:
    • 指针碰撞:没有内存碎片
    • 空闲列表:有内存碎片
  • 初始化零值:将分配到的内存空间都初始化为零值
  • 设置对象头:相当于这个对象的一个标签,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息
  • 执行init方法,把对象按照程序员的意愿进行初始化

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

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

相关文章

[机器视觉]halcon十二 条码识别、字符识别之字符识别

[机器视觉]halcon十二 条码识别、字符识别之字符识别 流程 获取图像-》创建模型-》查找文本-》清除模型 效果 算子 create_text_model_reader : 创建文本模型 find_text : 查找文本 get_text_result :获取文本内容 set_text_model_param : 设置文本模板…

5G与智慧文旅的融合发展:推动旅游业转型升级与可持续发展

随着5G技术的飞速发展和广泛应用,其与智慧文旅的融合发展正成为推动旅游业转型升级与可持续发展的重要力量。5G技术以其高速率、低时延、大连接的特性,为智慧文旅注入了新的活力,助力旅游业实现更高效、更智能、更绿色的发展。本文将深入探讨…

大白话说---“消息队列”

目录 一、什么是消息队列? 二、消息队列的作用 1.解耦 2.削峰 3.异步 三、消息队列的使用场景 1.传统设计 2.加入消息队列后的优化 四、常见的消息队列 一、什么是消息队列? 从名称上,我们就可以得到两个关键信息,即“消息”和…

去电脑维修店修电脑需要注意什么呢?装机之家晓龙

每当电脑出现故障时,你无疑会感到非常沮丧。 如果计算机已过了保修期,您将无法享受制造商的免费保修服务。 这意味着您必须自费找到一家电脑维修店。 去电脑维修店并不容易。 大家一定要知道,电脑维修非常困难,尤其是笔记本电脑维…

ElasticSearch之通过search after和scroll解决深度分页问题

写在前面 通过from,size来进行分页查询时,如下: 当from比较大时会有深度分页问题,问题产生的核心是coordinate node需要从每个分片中获取fromsize条数据,当from比较大,整体需要获取的数据量也会比较大&am…

C语言第三十六弹---文件操作(中)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 文件操作 1、文件的顺序读写 1.1、顺序读写函数介绍 1.1.1、fgetc 与 fputc 1.1.2、fgets 与 fputs 1.1.3、fscanf 与 fprintf 1.1.4、fread 与 fwrite 1.…

人工智能(AI)与电网系统的结合

人工智能(AI)与电网系统的结合可以带来许多潜在的好处,包括提高电网的运行效率、安全性和可靠性。以下是一些主要的应用领域,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交…

DevStack 基于 Ubuntu 部署 OpenStack

Devstack 简介 DevStack 是一系列可扩展的脚本,用于基于 git master 的最新版本快速调出完整的 OpenStack 环境。devstack 以交互方式用作开发环境和 OpenStack 项目大部分功能测试的基础。 devstack 透过执行 stack.sh 脚本,搭建 openstack 环境&…

Guitar Pro 8.1中文版永久许可证激活2024最新24位注册激活码生成器

Guitar Pro是一款非常受欢迎的音乐制作软件,它可以帮助用户创建和编辑各种音乐曲谱。从其诞生以来就送专门为了编写吉他谱而研发迭代的。 尽管这款产品可能已经成为全球最受欢迎的吉他打谱软件,在编写吉他六线谱和乐队总谱中始终处于行业领先地位&#…

“色狼”用英语怎么说?柯桥日常英语,成人英语口语学习

最近有粉丝问我"色狼"英文翻译是啥 首先声明不是"colour wolf"哈 关于“色狼”的英文表达有很多 快和C姐一起来看看吧! 1.pervert 这个单词的意思是变态、色狼 是对性变态者最直观的描述 He is such a pervert! I saw him lo…

VMware 集群-虚拟机配置反亲和性(互斥)

简介 博客:https://songxwn.com/ 为实现应用系统的冗余,经常会双机或者多机部署(如数据库集群等)。在VMware 集群里面,要保证不同应用集群的节点虚拟机在不同的物理宿主机上,防止单个宿主机故障&#xff…

数据结构之单链表详解(C语言手撕)

​ 🎉个人名片:🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN…

【开源】SpringBoot框架开发数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏,包含了GDP、…

连接kafka报错:java.io.IOException: Can‘t resolve address:

修改电脑host文件:C:\Windows\System32\drivers\etc\hosts 加上一行 192.168.1.XXX MHA_SLAVE2(192.168.1.XXX 这个是安装kafka 的服务器地址,MHA_SLAVE2是kafka的容器id)

【数据结构与算法】二分查找题解(二)

这里写目录标题 一、81. 搜索旋转排序数组 II二、167. 两数之和 II - 输入有序数组三、441. 排列硬币四、374. 猜数字大小五、367. 有效的完全平方数六、69. x 的平方根 一、81. 搜索旋转排序数组 II 中等 已知存在一个按非降序排列的整数数组 nums ,数组中的值不必…

c++ 二分查找(迭代与递归)

二分搜索被定义为一种在排序数组中使用的搜索算法,通过重复将搜索间隔一分为二。二分查找的思想是利用数组已排序的信息,将时间复杂度降低到O(log N)。 二分查找算法示例 何时在数据结构中应用二分查找的条件: 应用二分查找算法:…

(C语言)sizeof和strlen的对比(详解)

sizeof和strlen的对⽐(详解) 1. sizeof sizeof是用来计算变量所占内存空间大小的, 单位是字节,如果操作数是类型的话,计算的是用类型创建的变量所占空间的大小。 sizeof 只关注占用内存空间的大小 ,不在乎内…

Rust结构体讲解学习,以及impl结构体方法和结构体关联函数

Rust 中的结构体(Struct)与元组(Tuple)都可以将若干个类型不一定相同的数据捆绑在一起形成整体,但结构体的每个成员和其本身都有一个名字,这样访问它成员的时候就不用记住下标了。元组常用于非定义的多值传…

表单提交 滚动到必填校验位置

handleCommit(flag) {this.$refs["form"].validate((valid, object) > {if (valid) {this.form.checkState flag;this.form.checkLevel 1;this.form.type 1; //规划this.form.filingsId this.form.id;checkFilings(this.form).then((response) > {this.$mo…

list链表的创建,排序,插入, test ok

1. 链表的建立&#xff0c;打印 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stack> #include <iostream> #include <string.h> #include <string>using namespace std;struct node {int data;s…