八股文总是忘?一张图牢记JVM内存结构|金三银四系列

news2024/10/5 22:24:07

金三银四又来啦!八股文还是得复习起来,最近准备把一些常见的八股文知识点聊聊。

本文详解了JVM内存结构和各个部分详细内容,应付面试绰绰有余!

点击上方“后端开发技术”,选择“设为星标” ,优质资源及时送达

JVM体系基本结构

学习JVM内存结构之前,首先要明白它在整个JVM体系中所处的位置。

JVM 体系主要是两个部分,JVM的内部体系结构分为三个子系统和两大组件,分别是:类装载器(ClassLoader)子系统、执行引擎子系统和GC子系统,组件是内存运行数据区域和本地接口。

类加载器子系统,GC子系统以及内存运行数据区域是我们研究的重点。

61a201904110b2bc37a2569e6c00fe7c.png

JVM 内存空间结构

JVM内存结构主要有五部分组成:程序计数器、方法区、虚拟机栈、本地方法栈、堆。

程序计数器(PC寄存器)

JVM中的程序计数器(Program Counter Register)又名 PC 寄存器,是线程私有的,Java虚拟机会为每个线程创建PC寄存器,与线程共存亡。PC寄存器是一块很小的内存空间(只存下一条指令的地址),几乎可以忽略不计。也是运行速度最快的存储区域。

5901c079b9b2f2be458c6c7a2846f685.png

作用:PC寄存器用来存储指向下一条指令的地址(即将要执行的指令代码),由执行引擎读取下一条指令。每执行一条指令 PC 都会自增,因此 PC 存储了指向下一条要被执行的指令地址。

注意,程序计数器并不是一个指针,它只是一个计数器,用于记录下一条要执行的字节码指令的位置,字节码指令通常是存储在方法区或者堆中的永久代(Permanent Generation)中。在方法区中,字节码指令存储在类的字节码文件中,当类被加载到内存中时,字节码文件就会被读取并存储到方法区中。在执行 Java 程序时,JVM 会从方法区中读取字节码指令,并将其解释执行或者编译执行。

可以看作是当前线程所执行的字节码的行号指示器,但是并不直接指向方法区的代码,他保存的地址才指向方法区字节码信息。

在任意时刻,一个 Java 线程总是在执行一个方法,这个方法称为当前方法。如果当前方法不是本地方法,PC寄存器总会执行当前正在被执行的指令,如果是本地方法,则PC寄存器值为Underfined。

Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟(即软件方面的)。

程序计数器是JVM中唯一一个没有 OOM异常的区域。

方法区(永久代)

方法区存储了每个类的信息(版本、字段、方法、接口等)、常量、静态变量、即时编译后的代码等数据。所有线程共享同一个方法区,因此访问方法区数据的和动态链接的进程必须线程安全。如果两个线程试图访问一个还未加载的类的字段或方法,必须只加载一次,而且两个线程必须等它加载完毕才能继续执行。

a9d6f20da0c4017696afceb03b043b92.png

永久代(Permanent Generation)是 HotSpot 的概念,方法区是 Java 虚拟机实现规范,JDK 1.7及之前,Hotspot 使用永久代实现方法区。JDK 1.8 开始 HotSpots 取消了永久代,引入元空间。元空间是直接存在内存中,不在 Java虚拟机中的,因此元空间依赖于内存大小,当然你也可以自定义元空间大小。

你可能对此有疑问。那么是不是就没有方法区了呢?

当然不是,方法区是一个规范,规范没变,它就一直在。那么取代永久代的就是元空间。原先永生代中类的元信息会被放入本地内存(元数据区,metaspace),将类的静态变量和内部字符串放入到java堆中。

在JDK1.7中存储在永久代的部分数据就已经转移到Java Heap或者Native memory。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native memory;字符串常量池(interned strings)转移到了堆中;JDK1.8开始,类的静态变量(class statics variables )转移到了Java heap;

eb2c4da13fd4832314475985796ceba3.png

方法区不需要连续的内存,可以选择固定大小或者可扩展。并且还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。

这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

为什么引入元空间

元空间(Metaspace)是在JDK 1.8中引入的。在JDK 1.8之前的版本中,JVM使用永久代(Permanent Generation)来存储类的元数据信息,如类的字节码、方法、字段等信息。但是永久代的大小是有限制的,当加载的类或者应用程序使用的字符串常量等数据量较大时,可能会导致永久代的内存不足,从而引发OutOfMemoryError等问题。为了解决这个问题,JDK 1.8引入了元空间,它将类的元数据信息存储在堆外内存中,不再依赖于永久代,从而避免了永久代的内存限制问题。同时,元空间还支持动态调整大小,可以根据需要动态增加或缩减空间大小,提高了应用程序的灵活性。但是如果超过机器内存,也会出现OOM。

可以使用如下参数来调节方法区的大小:

JDK1.8之前调节方法区大小:

-XX:PermSize=N //方法区(永久代)初始大小

-XX:MaxPermSize=N //方法区(永久代)最大大小,超出这个值将会抛出OutOfMemoryError

JDK1.8开始方法区(HotSpot的永久代)被彻底删除了,取而代之的是元空间,元空间直接使用的是本机内存。参数设置:

-XX:MetaspaceSize=N //设置Metaspace的初始(和最小大小)

-XX:MaxMetaspaceSize=N //设置Metaspace的最大大小

虚拟机栈

Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部存储一个个用于线程执行方法的栈帧(Stack Frame),每个方法在执行时都会创建一个栈帧并推入虚拟机栈顶。栈是线程私有的,生命周期和线程一致,也就是线程结束了。

ef760b645fa27a736df769112fbe560d.png

虚拟机栈中主要存放以下内容:

  1. 局部变量表:用于存储方法执行过程中的局部变量,包括基本数据类型、对象引用(类、数组、接口)以及returnAddress类型(指向方法返回地址的指针)。

  2. 操作数栈:用于存储方法执行过程中的操作数,包括方法参数、局部变量以及运算时的临时变量等。

  3. 方法返回地址:用于存储方法调用后的返回地址,以便方法执行完成后能够返回到原来的调用位置。

  4. 动态链接:用于存储方法调用的动态链接信息,包括指向该方法所在类的常量池中的方法符号引用和该方法在运行时常量池中的直接引用等。

栈上分配是什么?

栈上分配是指在方法调用时,如果一个对象满足一些特定条件(如对象的生命周期非常短,不逃逸出当前方法等),那么可以将这个对象分配在虚拟机栈中,而不是在堆中进行分配。这种方式称为栈上分配。

相比在堆中分配,栈上分配的优势在于可以避免对堆内存的频繁申请和释放,减少了垃圾回收器的负担,从而提高了程序的执行效率。

需要注意的是,虚拟机并不是所有的对象都可以进行栈上分配,只有在编译器能够确定对象的生命周期和作用域不会逃逸出当前方法时,才能进行栈上分配。否则,对象必须在堆中进行分配,并由垃圾回收器负责管理和回收。

栈上分配是一项高级优化技术,在某些情况下可以显著提高程序的性能。但需要注意的是,过度依赖栈上分配可能会导致栈溢出等问题,因此应该谨慎使用。

栈中可能出现的异常

Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的。

  • 如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackOverflowError(给的固定内存不够) 异常。

  • 如果Java虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那Java虚拟机将会抛出一个 OutOfMemoryError (无法申请足够的内存)异常。

本地方法栈

Java虚拟机栈于管理Java方法的调用,而本地方法栈用于管理本地方法(Native Method)的调用。本地方法栈,也是线程私有的。

允许被实现成固定或者是可动态扩展的内存大小。在内存溢出方面与Java栈是相同的,可能出现 StackOverflowError 和 OutOfMemoryError。

如果线程请求分配的栈容量超过本地方法栈允许的最大容量,Java虚拟机将会抛出一个StackOverflowError 异常。如果本地方法栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的本地方法栈,那么Java虚拟机将会抛出一个OutOfMemoryError异常。(上面两点跟虚拟机栈一样)

本地方法是使用C语言实现的。

它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库(本地方法压入本地方法栈,然后给执行引擎获取(动态链接的方式),然后调用)。

堆(Heap)

应用系统对象都保存在Java堆中,堆被用来在运行时分配类实例、数组。堆上存储的对象在方法结束时不会被移除,只能由垃圾回收器移除。堆也是Java垃圾收集器管理的主要区域(所以很多时候会称它为GC堆),所有线程共享。

从GC回收的角度看,由于现在GC基本都是采用的分代收集算法,所以堆内存结构还可以分块成:新生代和老年代;再细一点的有Eden空间、From Survivor空间、To Survivor空间

7c7d7863e59f0b89a954c9e565a25d8f.png

为了给对象在内存中分配一块确定大小的空间,有两种堆内分配内存的方法,选择哪种分配方式由Java堆是否规整决定。

  1. 指针碰撞(Bump the Pointer):在堆内存中,用一个指针作为分界点,一侧为已经分配的内存,另一侧则是未分配的内存,当要分配内存时,只需要将指针向未分配的内存移动一段与对象大小相等的距离即可,分配出一块连续的内存空间来存储对象。(Serial、ParNew等带压缩过程的收集器采用)

  2. 空闲列表(Free List):在堆内存中,已经分配的内存和未分配的内存是交织在一起的,没有分界点,为了分配内存,需要维护一个空闲列表(Free List),记录哪些内存块是可用的,当需要分配内存时,从空闲列表中找到一块足够大小的空闲内存,然后将其分配给对象。分配内存后,需要从空闲列表中删除已分配的内存块,如果内存块被释放,那么需要将其加入到空闲列表中,方便下一次分配使用。(CMS这种基于标记清除Mark-Sweep算法的收集器采用)

总结

最后,我用一张图来总结今天的知识,大家收藏即可。

4c3800208f9bfcb794ddb19d884bc0e8.png

最后,欢迎大家提问和交流。

如果对你有帮助,欢迎点赞、评论或分享,感谢阅读!

无需注册直接体验ChatGPT!附注册ChatGPT详细教程

2023-02-09

655159fbe0a72e54ea2ede3dadf516f3.jpeg

一文掌握,单机Redis、哨兵和Redis Cluster的搭建,建议收藏

2023-02-11

c4160de435cbd1665ba788c69acfe20c.jpeg

MySQL事务ACID都知道,原理是什么?附面试题

2023-02-07

072bd0cf3545ed052ee1c4ac055b5586.jpeg

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

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

相关文章

visual studio2022配置opencv

标题:在vs下配置使用opencv 流程: 1、下载安装opencv 2、添加环境变量 3、vs中配置属性 4、使用 5、可能遇到的报错和解决 1、 下载安装opencv 官网下载地址: https://opencv.org/releases/ 我这里是windows环境,所以选择点击w…

Java数据结构之优先级队列

前言总是忘了优先队列的一些应用,其实这都是很基础的了。为了再加强印象,抄一遍吧。PriorityQueue简介PriorityQueue,即优先级队列。优先级队列可以保证每次取出来的元素都是队列中的最小或最大的元素(Java优先级队列默认每次取出…

微信小程序 if语法、for循环 条件渲染、列表渲染等讲解

这篇文章我想给大家学习的就是如何使用 if 去判断 组件的是显示和隐藏,如何使用for循环来渲染列表等重复的内容。 1.if语法的使用 在小程序中,我们可以使用wx:if"{{条件}}"来判断是否需要渲染该代码块 也可以用 wx:elif 和wx:else 来添加 el…

【java】Spring Boot -- Spring的IOC实现原理

文章目录IOC定义理解IOC不使用IOC:使用IOC:使用IOC的好处IOC提供被依赖对象的方式构造器注入setter 方法注入接口方式注入简单模拟IOC总结IOC定义 IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI&…

【Selenium】十分钟手把手带你学会WebDriver API

目录 1、定位元素【8种】 2、操作测试对象 3、添加等待 4、弹窗类型 5、浏览器的操作 6、键盘事件 7、选择框 8、上传文件 1、定位元素【8种】 元素定位是自动化测试的核心,想要去操作一个对象,第一步就是需要我们先去识别这个对象。每个对象就会…

全民ChatGPT热:快来解锁你的“全能网友”

前 言 2021年11月30日,OpenAI推出人工智能聊天原型ChatGPT,赚足了眼球,在AI界引发了类似AIGC让艺术家失业的大讨论。 据报道,ChatGPT在开放试用的短短几天,就吸引了超过 100 万互联网注册用户。并且社交网络流传出各种…

html5标签

图片:image 主要属性: src:源属性的值是图像的 URL 地址。 alt:用来为图像定义一串预备的可替换的文本。 注意事项: 注意: 假如某个 HTML 文件包含十个图像,那么为了正确显示这个页面,需要加…

速通Spring

尚硅谷2023最新版Spring6课程_bilibili 1 Spring 【强制】Spring是什么? 1) Spring是一款主流的Java EE轻量级开源框架。 轻量级:体积很小,且不需要依赖于其他组件。 2) 狭义的Spring。 Spring Framework。 3) 广义的Spring。 以Spring F…

python对多个csv文件进行合并(表头需一致)

之前写过python对【多个Excel文件】中的【单个sheet】进行合并,参考:点我 之前也写过python对【多个Excel文件】中的【多个sheet】进行合并,参考:点我 今天再写一个python对多个csv格式的文件进行合并的小工具 但是大家切记&am…

GIS开源框架:ArcGIS文件地理数据库(GDB)解析与入库

对于GIS专业毕业的同学,想必对于ArcGIS软件不会太陌生,对于地理数据库也有一定的了解和使用经验。但是,撇开软件操作层面不谈,作为一个WebGIS/GIS开发人员,我们如何通过GIS开源框架去完成地理数据库的自动化解析和入库…

解决不同影像裁剪后栅格数据行列不一致问题

前言在处理栅格数据时,尽管用同一个矢量文件裁剪栅格数据,不同数据来源的栅格行列数也会出现不一致的情况。如果忽略或解决不好,会导致后续数据处理出现意想不到的误差或错误,尤其是利用编程实现数据处理时。因此,应当…

VisualGDB 5.6R9 FOR WINDOWS

Go cross-platform with comfort VisualGDB 是 Visual Studio 的一个非常强大的扩展,它允许您调试或调试嵌入式系统。这个程序有一个非常有吸引力的用户界面,它有许多调试或调试代码的功能。VisualGDB 还有一个向导可以帮助您调试程序,为您提…

【C++】关键字、命名空间、输入和输出、缺省参数、函数重载

C关键字(C98)命名空间产生背景命名空间定义命名空间使用输入&输出缺省参数什么叫缺省参数缺省参数分类函数重载函数重载概念C支持函数重载的原理--名字修饰C关键字(C98) C总计63个关键字,C语言32个关键字。 下面我们先看一下C有多少关键字,不对关键…

Linux 解压JAR包 查看class内容

快速解决方案 查询class相对路径:jar tf test.jar | grep "test.class"单独解压class文件:jar xvf test.jar com/test/test.class查看class文件内容:javap -c com/test/test.class 背景 服务运行后,日志打印出来发现…

【taichi】利用 taichi 编写深度学习算子 —— 以提取右上三角阵为例

本文以取 (bs, n, n) 张量的右上三角阵并展平为向量 (bs, n*(n1)//2)) 为例,展示如何用 taichi 编写深度学习算子。 如图,要把形状为 (bs,n,n)(bs,n,n)(bs,n,n) 的张量,转化为 (bs,n(n1)2)(bs,\frac{n(n1)}{2})(bs,2n(n1)​) 的向量。我们先写…

各种素材网站大全【全部倾倒,福利倒计时-JS,HTML,游戏素材,UI,图片素材等

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:解忧杂货铺 ⭐各种素材网站大全⭐ 文章目录⭐各种素材网站大全⭐🎶大家必逛的四大天王…

STM32F769BIT6微控制器STM32F769IGT6详细规格

说明STM32F7 32 位 MCUFPU 基于高性能的 ARMCortex-M7 32 位 RISC 内核,工作频率高达 216MHz。Cortex-M7 内核具有单浮点单元(SFPU)精度,支持所有 ARM 单精度数据处理指令与数据类型。同时执行全套 DSP 指令和存储保护单元(MPU)&a…

JVM02类加载子系统

1. 加载阶段 通过一个类的全限定名获取定义此类的二进制字节流 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口 加载class文件的方式 从本地系统中直接…

六、HTTP 首部字段

HTTP 首部字段 一、HTTP 报文首部 HTTP 请求报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。 HTTP 响应报文由HTTP版本、状态码(数字和原因短语)、HTTP首部字段3部分构成。 HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端…

TensorRT如何工作

TensorRT如何工作 本章提供了有关 TensorRT 工作原理的更多详细信息。 文章目录TensorRT如何工作5.1. Object Lifetimes5.2. Error Handling and Logging5.3 Memory5.3.1. The Build Phase5.3.2. The Runtime Phase5.4. Threading5.5. Determinism5.1. Object Lifetimes Tenso…