Java虚拟机(JVM)知识点总结

news2024/12/1 12:40:04

一. Java内存区域

1. JVM的内存区域划分,以及各部分的作用

可分为运行时数据区域和本地内存,按照线程私有线程共享分类:

线程私有:程序计数器、虚拟机栈、本地方法栈。

线程共享:堆、方法区、直接内存。

JDK1.7与1.8版本略有不同。

1.8中,方法区被划分到了本地内存,并以元空间的形式存在。

(1)程序计数器

主要用来依次读取指令,实现代码的流程控制;同时还可以记录当前线程的位置,使线程切换后能够恢复到正确的位置。

(2)虚拟机栈

主要用来实现Java方法的调用与执行。栈由多个栈帧组成, 每个栈帧由局部变量表、操作数栈、动态链接、方法返回地址构成。

(3)本地方法栈

主要用来实现本地方法的调用与执行。

(4)堆

线程共享的一块内存区域,主要用于存放新创建的对象实例,几乎所有对象都在这里分配内存;也是垃圾回收的主要区域,也可称为GC堆。

从垃圾回收的角度来说,堆还可细分为新生代和老年代。再细分的话,新生代有Eden区、Survivor区、Old区。

下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。

JDK1.8版本后,永久代被元空间取代。

对象的存活的区域大致可以描述为,新创建的对象在Eden区,当进行一次新生代垃圾回收后,如果对象还存活,就会进入Survivor区(S0、S1),如果年龄继续增加,就会进入老年代。

(5)方法区

JVM运行时数据区的一块逻辑区域,被线程共享。主要用来存放类信息、方法信息、常量、静态变量等。

永久代和元空间是方法区的具体实现。

 为什么要将永久代替换为元空间?

  • 永久代有一个JVM本身设置的固定大小上限,无法调整,元空间使用的本地内存,虽然也可能会发生内存溢出,但概论相对较小。
  • 元空间里存放的是类的元数据,由系统实际可用的空间来控制,能够加载更多的类。

(6)运行时常量池

用于存放编译期生成的各种字面量和符号引用的常量池表,常量池表会在类加载后存到方法区的运行时常量池中,它的功能类似于符号表。

(7)字符串常量池

为了提升性能与减少内存消耗,避免字符串的重复创建。

JDK1.7之前, 字符串常量池存放在永久代中, 为什么1.7后移动到堆中?

主要是因为永久代(方法区的具体实现)的垃圾回收效率太低,只有在Full GC时才会被GC。Java中有大量的字符串等待回收,放到堆中能够及时有效的回收字符串内存。

(8)直接内存

位于本地内存中,并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。

2. Java对象的创建过程

(1)类加载检查

new指令——检查指令参数是否能在常量池中定位到符号引用——检查符号引用代表的类是否被加载过——若没有——执行类加载过程。

(2)分配内存:

类加载通过后进行内存分配,两种方式,指针碰撞和空闲列表。选择哪种方式由Java堆是否完整决定,Java堆是否完整由采用的GC收集器是否具有压缩整理功能决定。

  • 指针碰撞

适用于堆完整。原理是用过的内存整合到一边,没用过的整合到另一边,中间有个分界指针,向着没用过的方向移动指针即可。

  • 空闲列表

适用于堆不完整。虚拟机会维护一个列表,列表中会记录哪些内存块可用,分配时,会找一个足够大的内存块分配给对象,然后更新列表。

内存分配并发问题:

  • CAS+失败重试:乐观锁。失败就重试,直到成功。这种方式保证了操作的原子性。
  • TLAB:在Eden区预留一块内存。分配内存时,先从TLAB中分配,如果不够,再用上述CAS进行分配。

(3)初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,保证了对象的实例字段在代码中可以不被赋值就直接使用。

(4)设置对象头

虚拟机对对象进行必要的设置,如对象是哪个类的实例、对象的哈希码、对象的GC分代年龄等信息,这些信息存放在对象头中。

(5)执行init方法

从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,init方法还没执行,所有字段都还为零。执行new指令后接着执行init方法,按照开发者的意愿进行初始化。

3. 什么是JVM堆溢出和栈溢出?

堆溢出指的是JVM的堆内存不足以分配新的对象时发生的溢出。

栈溢出指的是JVM虚拟栈空间不足以支持新的方法调用时发生的溢出。

二. JVM垃圾回收机制

1. 堆内存的常见分配策略

  • 对象优先在在Eden区分配。
  • 大对象直接进入老年代。
  • 长期存活的对象将进入老年代。

2. 死亡对象判断方法(是否可以被GC回收)

(1)引用计数法

给对象中添加一个引用计数器。

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

实现简单,效率高,但使用较少,因为无法解决对象间的循环引用问题(两个对象互相引用导致计数器不为0)。

(2)可达性分析法

以 "GC Roots" 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。

哪些对象可以作为GC Roots呢?

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象。
  • 本地方法栈(Native方法)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。

3. Java中四种引用类型

(1)强引用:大部分引用都是强引用,最普遍的引用。new出来的对象都是强引用,即使内存空间不足,也不会被GC回收。

(2)软引用:SoftReference修饰。比强引用弱一些。内存空间不足时才会回收它。

(3)弱引用:WeakReference修饰。比软引用弱。无论内存空间充足与否,只要发现了弱引用就会被GC回收。

(4)虚引用:PhantomReference修饰。最弱的引用。没有实际作用,任何时候都能被回收,主要用来跟踪对象被垃圾回收的活动。

实际中使用软引用较多,软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出等问题的产生

4. 如何判断一个常量是废弃常量?

运行时常量池主要被回收的是废弃常量。

假如在字符串常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量。

5. 如何判断一个类是无用类?

方法区主要被回收的是无用的类。

需同时满足3个条件:

  • 该类所有的实例都已被回收。
  • 加载该类的ClassLoader已被回收。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

6. 4种垃圾收集算法

(1)标记—清除算法

标记和清除两个阶段。首先标记出所有不需要回收的对象,然后标记完成后对未标记的对象统一回收。这是基础算法,后续算法都是对其的改进。

但存在两个明显问题:效率问题(两阶段效率都不高)和空间问题(清除后存在大量不连续的内存碎片)。

(2)复制算法

为了解决效率和内存碎片问题,复制算法将内存分为大小相同的两块,每次使用其中的一块,其中一块使用完成后,将还存活的对象复制到另一块中去,然后再把使用的空间进行清理。保证每次的内存回收都是对一半区域的回收。

但依然存在问题:可用内存缩小为原来的一半。

(3)标记—整理算法

标记过程与标记—清除算法一样,但在标记完成后,让所有存活的对象向一端移动,然后清理掉端边界以为的内存。

(4)分代收集算法

主流算法。根据对象存活周期的不同将内存分为几块,一般将Java堆分为新生代和老年代,根据各个年代的特点选择合适的回收算法。

比如新生代,对象创建的多,但回收时死去的对象也很多,因此可以选择复制算法。老年代中的对象存活比较多,所以选择标记—清除或标记—整理算法。

7. 8个垃圾收集器

(1)Serial收集器

最基本、最悠久。单线程收集器,在进行垃圾收集工作时会暂停其它所有工作线程(Stop The World),直到结束。

简单而高效。

(2)ParNew收集器

Serial收集器的多线程版本,其余都和Serial一样。

(3)Parallel Scavenge收集器

几乎和ParNew收集器一样,但更关注吞吐量,高效利用CPU。

(4)Serial Old收集器

Serial收集器的老年代版本。

(5)Parallel Old收集器

Parallel Scavenge收集器的老年代版本。

(6)CMS收集器

CMS(Concurrent Mark Sweep ),以获取最短回收停顿时间为目标的收集器,注重用户体验。第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程基本上同时工作

标记清除算法的实现,运作过程大致分为四个步骤:

  • 初始标记:暂停其它所有线程,记录下与GC Root相连的对象。
  • 并发标记:同时开启GC和用户线程,用一个闭包结构记录可达对象。但GC线程无法保证可达性分析的实时性,会有引用更新的地方。
  • 重新标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的标记记录。
  • 并发清除:开启用户线程,同时GC线程对未标记的区域清除。

并发收集,低停顿。

(7)G1收集器

面向服务器的垃圾收集器,主要针对配备了多颗处理器及大容量内存的机器。既能满足低停顿还可以做到高吞吐量。

特点:

  • 并行与并发:充分利用多核的硬件优势来缩短停顿时间,也能做到和用户线程的并发执行。
  • 分代收集:不需要其它收集器配合就能独立管理整个GC堆。
  • 空间整合:整体标记—整理,局部标记—复制。
  • 可预测的停顿:除了降低停顿外,还能够建立可预测的停顿时间模型,让用户指定停顿时间。

步骤:

  • 初始标记
  • 并发标记
  • 最终标记
  • 筛选回收

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region,这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率。

JDK9之后,G1变成了默认的GC。

(8)ZGC收集器

采用复制算法,将暂停时间控制在几毫米以内,且暂停时间不受内存堆大小的影响,代价是牺牲了一些吞吐量。

8. Minor GC和Full GC

Minor GC主要发生在新生代,频繁且速度快;Full GC主要发生在老年代,回收速度较慢。一般来说,对象在新生代的Eden区分配。当Eden区没有足够空间分配时,虚拟机会进行⼀次Minor GC,Minor GC之后survivor放不下,要放到老年代,此时发现老年代也放不下,就会触发Full Gc。

三. 类加载器

1. 类加载器有哪些?

类加载过程:加载—连接—初始化。

连接又分为三步:验证—准备—解析。

类加载器作用于第一步加载。

类加载器的主要作用就是加载 Java 类的字节码(. class文件)到 JVM 中(在内存中生成一个代表该类的Class对象)。

JVM中内置了三个重要的ClassLoader:

  • BootstrapClassLoader(启动类加载器):最顶层的加载类,加载核心库:JAVA_HOME/jre/lib目录下的库。
  • ExtensionClassLoader(扩展类加载器) :加载扩展类的类库;JAVA_HOME/jre/lib/ext。
  • AppClassLoader(应用程序类加载器) :面向用户的加载器,加载classpath下的类。
  • 自己编写的类加载器。

除了 BootstrapClassLoader,其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader。如果要自定义自己的类加载器,也需要继承 ClassLoader 抽象类。

2. 双亲委派模型

类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载呢?

双亲委派模型和上面提到的类加载器层次关系图一致。

大致流程:加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,继续向上委托,如果这个类委托的上级没有被加载,子加载器会尝试加载这个类

  • 在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。
  • 加载的时候,首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父类加载器的loadClass()方法来加载类),所有的请求最终都会传送到顶层的启动类加载器BootstrapClassLoader中。
  • 当父类加载器无法处理这个加载请求(它的搜索范围中没有找到所需的类),子加载器才会尝试自己加载(调用自己findClass()方法来加载类)。
  • 子加载器也无法加载,抛出ClassNotFoundException异常。

双亲委派模型的好处?

  • 保证Java程序的稳定运行,避免重复加载类(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类)
  • 保证Java的核心API不被篡改。比如自己编写了一个java.lang.Object类,程序运行时就会有两个不同的Object类。双亲委派模型可以保证加载的时JRE里的Object类,而不是自己写的。因为AppClassLoader加载自己写的Object类时,会先委派给它的父类,即ExtClassLoader,而Ext又会委派给Boot,Boot发现自己加载过Object类了,会直接返回,而不是加载自己写的。

如何打破双亲委派模型?

继承ClassLoader类,自定义一个加载器,然后重写 loadClass() 方法。

之所以重写loadClass方法是因为加载类时,类加载器会先委托父类加载器去完成,即调用父类的loadClass()方法。

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

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

相关文章

V R虚拟现实元宇宙的前景|虚拟现实体验店加 盟合作|V R设备在线购买

VR(虚拟现实)技术作为一种新兴的技术,正在逐渐改变人们的生活和工作方式。随着技术的不断进步,人们对于元宇宙的概念也越来越感兴趣。元宇宙是一个虚拟世界,通过VR技术可以实现人们在其中进行各种活动和交互。 元宇宙的…

(C++17) std算法之执行策略 execution

文章目录 前言Code测试Code运行效果 msvc源码描述源码std::sequenced_policy seqstd::parallel_policy parstd::parallel_unsequenced_policy par_unseqstd::unsequenced_policy unseq END 前言 ref:算法库-执行策略 - cppreference.com 利用多核cpu加速算法在目前看来已经不是…

Springboot+MybatisPlus+EasyExcel实现文件导入数据

记录一下写Excel文件导入数据所经历的问题。 springboot提供的文件处理MultipartFile有关方法,我没有具体看文档,但目测比较复杂, 遂了解学习了一下别的文件上传方法,本文第1节记录的是springboot原始的导入文件方法写法&#xf…

论文阅读-《Lite Pose: Efficient Architecture Design for 2D Human Pose Estimation》

摘要 这篇论文主要研究了2D人体姿态估计的高效架构设计。姿态估计在以人为中心的视觉应用中发挥着关键作用,但由于基于HRNet的先进姿态估计模型计算成本高昂(每帧超过150 GMACs),难以在资源受限的边缘设备上部署。因此&#xff0…

vue的创建、启动以及目录结构详解

vue的创建、启动以及目录结构详解目录 一. vue项目的创建 二. vue的目录结构 三. src的目录结构 四. vue项目的启动 4.1 方法1 4.2 方法2 一. vue项目的创建 创建一个工程化的Vue项目,执行命令:npm init vuelatest 注意:如果你在这个目…

【Python基础教程】3 . 算法的时间复杂度

🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:python基础教程 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、…

[每周一更]-第91期:认识AMD的CPU

这一章来认识下AMD,说起AMD,从我上大学那时候,就选购的AMD 速龙系列,生活拮据的情况下,拉着室友去郑州电子城,在跟奸商老板的拉扯下,斥资2000购入一个无显卡 的台式机(实在是资金有限&#xff0…

【Spring源码】WebSocket做推送动作的底层实例

一、前瞻 Ok,开始我们今天的对Spring的【模块阅读】。 那就挑Web里的WebSocket模块,先思考下本次阅读的阅读线索: WebSocket在Spring里起到什么作用这个模块采用了什么设计模式我们都知道WebSocket可以主动推送消息给用户,那做推…

小程序中使用less

在vscode中安装插件 找到左下角齿轮的设置,点击右边图标,进入“settings.json” 加上以下代码配置 "less.compile":{"outExt": ".wxss"}

在.Net6中用gdal实现第一个功能

目录 一、创建.NET6的控制台应用程序 二、加载Gdal插件 三、编写程序 一、创建.NET6的控制台应用程序 二、加载Gdal插件 Gdal的资源可以经过NuGet包引入。右键单击项目名称,然后选择 "Manage NuGet Packages"(管理 NuGet 包)。N…

揭秘情绪识别:如何让AI读懂你的心声?

最近我在研究大语言模型,想用它来给样本打分。 起初,我尝试让模型用1到5分来评分,但它总是极端地给出最低分或最高分,评分缺乏中间地带。 于是我换了个方法,不再用数字,而是用描述性的词语,比…

《让你的时间多一倍》逃离时间陷阱,你没有自己想的那么懒 - 三余书屋 3ysw.net

让你的时间多一倍 今天我们来阅读法比安奥利卡尔的作品《让你的时间多一倍》。或许你会心生疑虑,这本书是否又是一本沉闷的时间管理指南?但我要告诉你的是,尽管时间管理这个话题已经为大众所熟知,这本书却为我们揭示了一个全新的…

php将网页用wkhtmltoimage内容生成为图片

php架构ThinkPHP6 1. 安装 knp-snappy架构 composer require knplabs/knp-snappy use Knp\Snappy\Image; use Illuminate\Support\Facades\Storage;// 生成图片 /user/local/bin/wkhtmltoimage为你的wkhtmltoimage的位置。 $snappy new Image(/usr/local/bin/wkhtmltoimage…

EXCEL VBA根据表数据写入数据库中

EXCEL VBA根据表数据写入数据库中 Option Explicithttps://club.excelhome.net/thread-1687531-1-1.htmlSub UpdateAccess()Const adStateOpen 1Dim vData, i As Variant, j As LongDim AccessTable As String, ExcelTable As String, ExcelFile As String, AccessFile As Str…

【JavaWeb】Day27.Web入门——Tomcat介绍

目录 WEB服务器-Tomcat 一.服务器概述 二.Web服务器 三.Tomcat- 基本使用 1.下载 2.安装与卸载 3.启动与关闭 4.常见问题 四.Tomcat- 入门程序 WEB服务器-Tomcat 一.服务器概述 服务器硬件:指的也是计算机,只不过服务器要比我们日常使用的计算…

HarmonyOS 应用开发之启动/停止本地PageAbility

启动本地PageAbility PageAbility相关的能力通过featureAbility提供,启动本地Ability通过featureAbility中的startAbility接口实现。 表1 featureAbility接口说明 接口名接口描述startAbility(parameter: StartAbilityParameter)启动Ability。startAbilityForRes…

vite vue3 import.meta.glob动态路由

在Vite中使用Vue 3,你可以使用import.meta.glob来导入目录下的多个Vue组件,并自动生成路由。以下是一个简单的例子: router/index.js // router/index.js import { createRouter, createWebHistory } from vue-router;// 自动导入views目录下…

【51单片机入门记录】定时器/计数器

目录 一、定时器/计数器概述 (1)实现定时功能的几种方法 (2)定时器和计数器的区别 (3)基本概念 二、定时器/计数器相关寄存器 (1)控制寄存器TCON(可位寻址&#xf…

软考高级软件架构师:数字签名技术概念和例题

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

【C++】多态的原理

目录 一、虚函数表 1、虚函数表的定义 2、虚函数表特性 3、虚表的打印 二、多态的原理 三、多态的相关问题 1、指针偏移问题 2、输出的程序是什么? 3、输出的程序是什么? 【前言】 上一篇我们学习了多态的基础知识,这一篇我将带着大…