JVM与GC

news2024/10/5 17:27:30

Java:跨平台的语言

write once, run anywhere

img

JVM:跨语言的平台

Java虚拟机根本不关心运行在其内部的程序到底是使用何种编程语言编写的,它只关心“字节码”文件。

Java不是最强大的语言,但是JVM是最强大的虚拟机。

img

JVM的整体结构

img

这个架构可以分成三层看:

  • 最上层:javac编译器将编译好的字节码class文件,通过java 类装载器执行机制,把对象或class文件存放在 jvm划分内存区域。
  • 中间层:称为Runtime Data Area,主要是在Java代码运行时用于存放数据的,从左至右为方法区(永久代、元数据区)、堆(共享,GC回收对象区域)、栈、程序计数器、寄存器、本地方法栈(私有)。
  • 最下层:解释器、JIT(just in time)编译器和 GC(Garbage Collection,垃圾回收器)

img

常见的JVM

  1. SUN公司的 HotSpot VM
  2. BEA 的 JRockit --> 不包含解释器,服务器端,JMC
  3. IBM 的 J9

类的加载

img

ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。

img

类的加载过程

见https://blog.csdn.net/weixin_44743245/article/details/129231581

类的加载器

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

img
这里的四者之间的关系是包含关系。不是上层下层,也不是子父类的继承关系。

在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。为什么要自定义类加载器?

  • 隔离加载类,避免类冲突
  • 修改类加载的方式,根据实际情况在某个时间点按需动态加载
  • 扩展加载源:网络、数据库、机顶盒
  • 防止源码泄漏

image-20230505233906275

双亲委派机制

image-20230506181543292

工作原理

1)如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
2)如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
3)如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。

优势

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改
    自定义类:java.lang.String
    自定义类:java.lang.ShkStart

内存结构篇

image-20230506200430910

image-20230506182552872image-20230506182559400


image-20230506182608425


image-20230506182744203

程序计数器

PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令。

java程序计数器的介绍和作用

虚拟机栈

image-20230506185823416

栈桢(Stack Frame)

栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。

image-20230506192507100

局部变量表

定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量

image-20230506193012083

这些数据类型包括各类基本数据类型、对象引用(reference),以及returnAddress类型

局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。

操作数栈

又称为表达式栈,后进先出。在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)/出栈(pop)。并非采用索引访问。

动态链接

  1. 每一个栈帧内部包含一个指向运行时常量池中该栈帧所属方法的引用,包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接
  2. 在java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。

本地方法栈

  • Java 虚拟机栈用于管理Java 方法的调用,而本地方法栈用于管理本地方法的调用。
  • 本地方法是使用C语言实现的。
  • 它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

image-20230506194608164

image-20230506194519919

所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer, TLAB)。

image-20230506194739725

在程序中,开发人员可以通过选项“-XX:UseTLAB”设置是否开启TLAB空间。
默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,当然我们可以通过选项“-XX:TLABWasteTargetPercent”设置TLAB空间所占用Eden空间的百分比大小。

对象分配过程

image-20230506194905327n欸村

内存分配策略

  • 优先分配到Eden

  • 大对象直接分配到老年代

  • 长期存活的对象分配到老年代

  • 空间分配担保(-XX:HandlePromotionFailure)

    在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,
    如果大于,则此次Minor GC是安全的
    如果小于,则虚拟机会查看-XX:HandlePromotionFailure设置值是否允许担保失败。
    如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。

    在JDK 6 Update 24之后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略,观察OpenJDK中的源码变化,虽然源码中还定义了HandlePromotionFailure参数,但是在代码中已经不会再使用它。JDK 6 Update 24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

  • 动态对象年龄判断

方法区

对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。所以,方法区看作是一块独立于Java 堆的内存空间。

image-20230506200240695

垃圾回收篇

垃圾回收算法

垃圾标记阶段算法

引用计数算法

原理:
对于一个对象A,只要有任何一个对象引用了A ,则A 的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A 的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。

优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。

缺点

  • 它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。
  • 每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
  • 引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一条致命缺陷,导致在Java 的垃圾回收器中没有使用这类算法。

image-20230506201231730

可达性分析算法

原理:
其原理简单来说,就是将对象及其引用关系看作一个图,选定活动的对象作为 GC Roots,然后跟踪引用链条,如果一个对象和GC Roots之间不可达,也就是不存在引用链条,那么即可认为是可回收对象。

优点:
实现简单,执行高效 ,有效的解决循环引用的问题,防止内存泄漏。

image-20230506201612522

垃圾清除阶段算法

标记-清除算法

标记:对存活的对象进行标记。
清除:清除没有标记的,也就是垃圾对象。

image-20230506203200545

缺点:
1、效率比较低:递归与全堆对象遍历两次
2、在进行GC的时候,需要停止整个应用程序,导致用户体验差
3、这种方式清理出来的空闲内存是不连续的,产生内存碎片。

复制算法

image-20230506203423894

优点:
没有标记和清除过程,实现简单,运行高效
复制过去以后保证空间的连续性,不会出现“碎片”问题。

缺点:
此算法的缺点也是很明显的,就是需要两倍的内存空间。
另外,对于G1这种分拆成为大量region的GC,复制而不是移动,意味着GC需要维护region之间对象引用关系,这个开销也不小,不管是内存占用或者时间开销。
应用场景:
在新生代,对常规应用的垃圾回收,一次通常可以回收70%-99%的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
比如:IBM 公司的专门研究表明,新生代中 80% 的对象都是“朝生夕死”的。

标记-压缩算法

复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活对象较多,复制的成本也将很高。因此,基于老年代垃圾回收的特性,需要使用其他的算法。

标记-清除算法的确可以应用在老年代中,但是该算法不仅执行效率低下,而且在执行完内存回收后还会产生内存碎片,所以JVM 的设计者需要在此基础之上进行改进。标记 - 压缩(Mark - Compact)算法由此诞生。

image-20230506203622117

优点:(此算法消除了“标记-清除”和“复制”两个算法的弊端。)
消除了标记/清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可。
消除了复制算法当中,内存减半的高额代价。

缺点:
从效率上来说,标记-压缩算法要低于复制算法。
效率不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
对于老年代每次都有大量对象存活的区域来说,极为负重。
移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址。
移动过程中,需要全程暂停用户应用程序。即:STW

分代收集算法

目前几乎所有的GC都是采用分代收集(Generational Collecting)算法执行垃圾回收的。
在HotSpot 中,基于分代的概念, GC 所使用的内存回收算法必须结合年轻代和老年代各自的特点。
年轻代(Young Gen)
年轻代特点是区域相对老年代较小,对象生命周期短、存活率低,回收频繁。
老年代(Tenured Gen)
老年代的特点是区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。

image-20230506203823000

分区算法

分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间

每一个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。
一般来说,在相同条件下,堆空间越大,一次GC时所需要的时间就越长,有关GC产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。

image-20230506204051543

4种引用

  • 强引用:不回收
  • 软引用:内存不足即回收
  • 弱引用:发现即回收
  • 虚引用:对象回收跟踪

垃圾回收器

  • 按线程数分,可以分为串行垃圾回收器和并行垃圾回收器。

  • 按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器。
    并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的停顿时间。
    独占式垃圾回收器( Stop the world)一旦运行,就停止应用程序中的其他所有线程,直到垃圾回收过程完全结束。

  • 按碎片处理方式可分为压缩式垃圾回收器和非压缩式垃圾回收器。
    压缩式垃圾回收器会在回收完成后,对存活对象进行压缩整理,消除回收后的碎片。
    非压缩式的垃圾回收器不进行这步操作。

  • 按工作的内存区间,又可分为年轻代垃圾回收器和老年代垃圾回收器。

GC评估指标

  • 吞吐量:程序的运行时间(程序的运行时间+内存回收的时间)。

  • 垃圾收集开销:吞吐量的补数,垃圾收集器所占时间与总时间的比例。

  • 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。

  • 收集频率:相对于应用程序的执行,收集操作发生的频率。

  • 内存占用: Java 堆区所占的内存大小。

  • 快速: 一个对象从诞生到被回收所经历的时间。

    吞吐量优先:单位时间内,STW的时间最短:0.2 + 0.2 = 0.4
    响应时间优先:尽可能让单次STW的时间最短:0.1 + 0.1 + 0.1 + 0.1 + 0.1 = 0.5

吞吐量、暂停时间、内存占用共同构成一个“不可能三角”。三者总体的表现会随着技术进步而越来越好。一款优秀的收集器通常最多同时满足其中的两项。
这三项里,低延迟的重要性日益凸显。

Serial GC:串行回收

Serial 收集器采用复制算法、串行回收和“ Stop-the-World ”机制的方式执行内存回收。

image-20230506205004293

**优势:**简单而高效(与其他收集器的单线程比),对于限定单个 CPU 的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

在HotSpot 虚拟机中,使用 -XX: +UseSerialGC 参数可以指定使用年轻代串行收集器和老年代串行收集器。
等价于 新生代用Serial GC,且老年代用Serial Old GC

ParNew GC:并行回收

如果说Serial GC是年轻代中的单线程垃圾收集器,那么ParNew 收集器则是Serial 收集器的多线程版本。
Par是Parallel的缩写,New:只能处理的是新生代

ParNew 收集器除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别,因为ParNew 收集器在年轻代中同样也是采用复制算法、“ Stop-the-World"机制。

image-20230506210950295

在程序中,开发人员可以通过选项“ -XX:+UseParNewGC ”手动指定使用ParNew 收集器执行内存回收任务。它表示年轻代使用并行收集器。

Parallel GC:吞吐量优先

HotSpot 的年轻代中除了拥有ParNew 收集器是基于并行回收的以外, Parallel收集器同样也采用了复制算法、并行回收和“ Stop the World ”机制。

那么Parallel收集器的出现是否多此一举?和ParNew 收集器不同,Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量(Throughput),它也被称为吞吐量优先的垃圾收集器。

image-20230506211140587

“ -XX:+UseParallelGC ”手动指定年轻代使用Parallel 并行收集器执行内存回收任务。
“ -XX:+UseParallelOldGC” 手动指定年轻代和老年代都是使用并行回收收集器。

CMS:低延迟

在 JDK 1.5 时期,HotSpot 推出了一款在强交互应用中几乎可认为有划时代意义的垃圾收集器——CMS (Concurrent-Mark-Sweep)收集器,这款收集器是 HotSpot 虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作。

在G1出现之前,CMS使用还是非常广泛的。

image-20230506211256135

-XX :+UseConcMarkSweepGC:手动指定使用CMS 收集器执行内存回收任务

G1 GC:区域化分代式

G1(Garbage-First)是一款面向服务端应用的垃圾收集器,兼顾吞吐量和停顿时间的GC实现。
在JDK1.7版本正式启用,是JDK 9以后的默认GC选项,取代了CMS 回收器。

image-20230506213838359

各GC使用场景

  • Serial收集器:串行运行;作用于新生代;复制算法;响应速度优先;适用于单CPU环境下的client模式。
  • ParNew收集器:并行运行;作用于新生代;复制算法;响应速度优先;多CPU环境Server模式下与CMS配合使用。
  • Parallel Scavenge收集器:并行运行;作用于新生代;复制算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  • Serial Old收集器:串行运行;作用于老年代;标记-整理算法;响应速度优先;单CPU环境下的Client模式。
  • Parallel Old收集器:并行运行;作用于老年代;标记-整理算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  • CMS收集器:并发运行;作用于老年代;标记-清除算法;响应速度优先;适用于互联网或B/S业务。
    )]

各GC使用场景

  • Serial收集器:串行运行;作用于新生代;复制算法;响应速度优先;适用于单CPU环境下的client模式。
  • ParNew收集器:并行运行;作用于新生代;复制算法;响应速度优先;多CPU环境Server模式下与CMS配合使用。
  • Parallel Scavenge收集器:并行运行;作用于新生代;复制算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  • Serial Old收集器:串行运行;作用于老年代;标记-整理算法;响应速度优先;单CPU环境下的Client模式。
  • Parallel Old收集器:并行运行;作用于老年代;标记-整理算法;吞吐量优先;适用于后台运算而不需要太多交互的场景。
  • CMS收集器:并发运行;作用于老年代;标记-清除算法;响应速度优先;适用于互联网或B/S业务。
  • G1收集器:并发运行;可作用于新生代或老年代;标记-整理算法+复制算法;响应速度优先;面向服务端应用。

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

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

相关文章

Java基础学习(12)

Java基础学习 一、不可变集合二、Stream流2.1 Stream流数据添加2.2 Stream流的中间方法2.3 Stream终结方法 三、 方法引用3.1 方法引用的基本概念3.2 方法引用的分类3.2.1 引用静态方法 3.2.2 引用成员方法3.2.3 引用构造方法3.2.4 使用类名引用成员方法3.2.5 引用数组的构造方…

PyQt5 基础篇(一)-- 安装与环境配置

1 PyQt5 图形界面开发工具 Qt 库是跨平台的 C 库的集合,是最强大的 GUI 库之一,可以实现高级 API 来访问桌面和移动系统的各种服务。PyQt5 是一套 Python 绑定 Digia QT5 应用的框架。PyQt5 实现了一个 Python模块集,有 620 个类,…

MATLAB 点云非均匀体素下采样 (8)

MATLAB 点云非均匀体素下采样的不同参数效果测试 (8) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、实现代码(详细注释!)五、与固定步长采样法比较5.1 代码5.2 效果一、实现效果 不同参数调整下的非均匀体素下采样结果如下图所示,后续代码复制黏贴即可: 可…

入职6个月,被裁了...

我跟大多数人不大一样,从来没有说要等公司主动裁员拿补偿,我看自己没有什么价值或者是公司不行了,我都会主动离职。但是这次也太突然了。公司很大已上市,并不是不行了,总结原因就是,一是领导无能&#xff0…

【STM32CubeMX】F103ADC获取

前言 本文记录了我学习STM32CubeMX的过程,方便以后回忆。我们使用的开发板是基于STM32F103C6T6的。本章记录了基本的ADC值的获取流程,只单纯地记录了ADC端口的配置,没有加配像串口之类的调试,以简化流程。下面的流程是使用串口调试…

版本控制系统Git - 配置与基本使用

Git 1 Git简介1 Git概述2 Git的作用2.1 项目版本管理2.2 多人协同开发2.3 Git 的结构2.4 Git的工作原理 2 Git安装1 下载Git2 安装Git3 配置环境变量4 测试git是否安装成功5 安装git桌面工具(可以不安装) 3 Git基本操作1 设置Git用户2 新建仓库3 查看仓库状态4 添加到暂存文件5…

MATLAB 点云重复点去除(7)

MATLAB 点云重复点去除 (7) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、具体代码 (注释详细!)一、实现效果 效果上看不出来,但实际上左边的点云是右边的两倍 二、算法介绍 重复点的去除,是点云处理中常用的预处理方法,因为重复点的存在有时候会严重干…

C++系列四:数组

数组 1. 数组定义与初始化2. 多维数组3. 字符数组4. 总结 1. 数组定义与初始化 定义数组时需要指定数组的类型和大小: int myArray[10];上述代码定义了一个包含 10 个整数的数组。这些整数的下标从 0 开始,并以 1 个单位递增。 C 允许在定义数组时对其…

设计模式——装饰者模式(继承和接口的两种实现方式)

是什么? 场景案例:想必大家都吃过手抓饼吧,我们在点手抓饼的时候可以选择加培根、鸡蛋、火腿、肉松等等这些配菜,当然这些配菜的价格都不一样,那么计算总价就比较麻烦; 装饰者模式就是指在不改变现有对象…

springboot 集成 shardingSphere 加mybatisplus 自带增加 分页查询 和源代码包 分库分表 单库 分表 使用雪花算法id

目录 介绍 代码下载 效果 数据库 代码结构 上代码 pom.xml yml配置 建表语句 mapper.xml mybatisplus 配置.java logback application.java BaseEntity TUser TUserMapper TUserService TUserServiceImpl TUserController 测试 介绍 这套springboot shardi…

OSG笔记:AutoTransform实现固定像素大小的图形

需求 在(200,0,0)位置绘制固定10像素大小的正方体 实现方式 为了便于观察,例子中绘制了两条直线,相交于(200,0,0)。 //两根直线交于(200, 0, 0),用于辅助观察 {osg::Geometry* pLineGeom new osg::Geometry();osg::Vec3Array* pVertexArra…

python语法复习

print:输出函数 print(520)效果:输出520. print(hello)效果:输出hello. print(1020)【效果:输出了:1020】注:“ ”在print里面是一个连接符。 print(1020)【效果:输出了30】注: 在此处…

OJ 系统常用功能介绍 快速入门 C++ Python JAVA语言在线评测

技术支持微 makytony 服务器配置 腾讯云 2H4G 5M 60GB 轻量应用服务器 承载大约 200~400人使用,经过压力测试,评测并发速度可满足130人左右的在线比赛。 系统镜像选 Ubuntu 22.04 LTS ,Ubuntu是最热门的Linux发行版之一,是一款…

洞车系统常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交,提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

spring、springmvc、springboot、mybatis框架高频面试题

一、spring 1、Spring框架中的单例bean是线程安全的吗? 不是线程安全的,是这样的: 当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求对应的业务逻辑(成员方法&a…

ES索引结构升级-笔记

ES中索引的字段类型是不可修改的,只能是重新创建一个索引并设置好mapping,然后再将老索引的数据复制过去 查看老索引mapping GET /twitter/_mappings创建new索引,并指定mapping PUT /twitter410{"mappings": {"properties&…

金3银四结束了,回顾一下我2个月面试的公司....

金三银四结束了,还没有 offer 的同学不要气馁,该来的迟早会来。楼主从 年底 月有想法跳槽开始准备春招,一开始也是惨不忍睹,后来慢慢进入状态最近的面试基本都能走到终面,所以好好坚持,最后一定会有好结果的…

操作系统考试复习——第四章 存储器管理 4.1 4.2

存储器的层次结构: 存储器的多层结构: 存储器至少分为三级:CPU寄存器,主存和辅存。 但是一般分为6层为寄存器,高速缓存,主存储器,磁盘缓存,固定磁盘,可移动存储介质。…

无需重复造轮子—借助 Jetpack 组件,加速应用程序开发

简介 Android Jetpack 是谷歌官方推出的一套 Android 应用架构组件库,旨在为 Android 应用开发提供一种更优秀、更高效且更简单的方式。 Android Jetpack 对开发者带来了许多好处,以下是其中的一些: 提高开发效率:Jetpack 提供…

vue项目重复点击一个路由会报错如何解决

在新版本的vue-router中,重复点击同一个路由会出现以下报错 : 这个问题时vue-router 3.2.0版本的一个小Bug 方法有很多,比如降低路由版本(不推荐),但是推荐下面这种方式,比较简单靠谱 // 把这段代码直接粘贴到router/index.js中的Vue.use(VueRouter)之…