JVM详解:垃圾回收机制

news2024/11/27 9:32:28

java作为大型服务开发的主流语言,其运行会占用大量的内存空间,那么合理的使用有限的服务器资源至关重要。和大多数翻译性语言一样,java的运行环境jvm也内置垃圾回收机制,其通过一些合理的算法组合,定时来对堆中保存的不可达的临时实例进行清除,来保证运行环境的内存空间。

首先我们要清楚,一切的垃圾回收都是针对于堆内存的,堆内存的作用是存储所有的临时对象(即使其可能存活时间较长),而垃圾回收则是查看这些临时对象时候到了可以被清除的时候。

垃圾回收算法

jvm中的垃圾回收设计多种算法的组合使用,具体如下:

1. 标记清除法

标记清除法通过从根节点开始,寻找实例对象引用,并进行标记,而对于未被标记的实例对象则会在清除阶段进行清除。根节点并非只有一个,java代码中,或jvm中存在的生命周期和jvm声明周期一致的内存引用都有可能垃圾回收寻找并标记对象的根节点,如方法中初始化的一个实例,静态变量,方法区中常量池中的常量,线程对象,jvm中保存的一些实例等。

在这里插入图片描述

2. 标记整理法

整理算法的原理简单,但不好口述。首先整理算法也需要对所有可达对象进行标记,过程和标记清除法一直,而整理阶段,整理算法是从内存的两侧开始查找,一侧查找没有被标记的可删除或已经删除当空闲的区域,而另一侧则查找被标记的可达对象,将可达对象放入覆盖可删除或已经删除的空闲区域,当两侧查找到相同位置时,则证明查找完成,所有的可达对象都已经被整理到了内存的一侧,而另一侧将被全部清除。
在这里插入图片描述

3. 复制算法

复制算法的原理更加简单,且高效,但代价是只能使用一半的内存空间。复制算法将堆内存分为两个部分,其中一侧为活动区,一侧为空闲区,活动区的内存正常保存对象实例,而空闲区完全空闲。当垃圾回收开始时,依旧会查找互动区的可达对象,不同的时此时不会执行标记操作,而是直接将其放入空闲区,等到整个活动区查找完成后,删除活动区的所有内容,活动区和空闲区身份替换,以此类推。

在这里插入图片描述

4. 分代收集算法

分代收集算法相对复杂,其将堆内存分为两个区域,一个是年轻代 (Young Generation),一个是老年代 (Old Generation)。当一个实例被创建时,会被放入年轻代 (Young Generation)中,随着垃圾回收的不断进行,年轻代 (Young Generation)中一部分实例一直存活,当这个次数达到一个阀值时,则认为这个实例可能存活时间较长,则会放入老年代 (Old Generation)中。老年代 (Old Generation)中的垃圾回收频率较低,所以通常使用标记清除法或标记整理法。而年轻代 (Young Generation)会频繁的进行垃圾回收,通常会选择复制算法,确保对java服务的性能影响较小。

年轻代 (Young Generation)中的复制算法和上面说的复制算法有些许不同,年轻代 (Young Generation)中的复制算法将年轻代 (Young Generation)内存分为了两个区域,分别是E区(Eden)和S区(Survivor),其中S区被分为S0和S1,E区永远为活动区,而S区则是一个活动区一个空闲区。当一个实例对象创建时会直接被放入E区。垃圾回收开始时,会扫描E区和S区的活动区中的可达对象,并将可达对象放入S区的空闲区,然后清空E区和S区的活动区。大多数的实例对象在使用一次后就会被销毁,而少部分的存活对象则被保存在S区中等待被清除或进入老年代 (Old Generation)。通过这种方式不仅有效的节省内存,而切还能够利用到复制算法的高效性,提高性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

操作系统通过分页解决了外部碎片问题,但这不意味着jvm的内存就没有外部碎片问题了,因为操作系统操作的是真实的内存地址,而jvm操作的是虚拟的连续内存地址,如果对这一块不了解,可以看我的操作系统相关文章。

标记清楚法会引起外部碎片问题,而标记整理和复制算法都不会,图片画的很清晰了,这里就不说了。

垃圾回收器(GC)

对于不同的垃圾回收算法的合理利用,可以出现不同的效果,jvm提供了多种垃圾回收器,需要根据实际情况选择最合适的一款使用。所有的垃圾回收机都会使用分代收集算法,但具体实现有一些不同。

了解GC之间的差异,我们需要了解两个重要的概念,分别的吞吐量和停顿(STW)。吞吐量指的是GC在执行回收工作时用的时间长短,时间越少证明吞吐量就会越高,而停顿则是说停止用户线程的时间。说到这就有了第一个嗯题,为什么要停止用户线程,用户线程和GC之间两个线程执行不就可以了吗?

无论是用户线程,还是GC线程,都是在操作堆中的对象实例,而处理两个线程对于堆中实例的操作都是耗时操作,所以有的GC为了维持垃圾回收时间更短,则会选择直接将线程停止,然后专心进行垃圾回收,这样会让整体效率变高,但用户侧会感受到明显卡顿。有的GC则相反。

针对这两个概念,我们就可以了解各个GC的区别,并择优选择合适的GC。

1. Serial GC(串行垃圾回收器)

串行垃圾回收器的所有工作是在一个线程中完成,并且会在垃圾回收的整个过程暂停用户线程的进行。其在老年代中使用的标记整理算法,新生代中使用复制算法。

其资源消耗最少,但吞吐量低,停顿时间长,在小型服务,并且资源有限的情况下可以尝试。

2. Parallel GC(并行垃圾回收器)

并行垃圾回收器采用多线程垃圾回收机制,其致力于提高吞吐量,而不管停顿时间。新生代采用的复制算法,老年代采用的标记整理法。这种架构使得其可以更快的垃圾回收速度,但用户停顿感依旧明显,停顿时间较长

3. CMS GC(Concurrent Mark-Sweep,标记清除回收器)

不同于其他的GC,CMS致力于老年代的区域回收,其新生代的回收依赖于并行垃圾回收器的新生代垃圾回收器部分。也就是说CMS老年代使用自身的标记清除法,而新生代则使用并行垃圾回收器的复制算法。

CMS致力于缩短用户线程的停顿时间,也就是说在CMS对老年代进行垃圾回收时,会尽可能的让用户线程与垃圾回收线程一同执行,其具体操作主要分为初始标记,并发标记,重新标记以及并发清除四个阶段:

  • 初始标记;初始标记时使用单线程标记根节点
  • 并发标记:并发标记是使用多线程,从根节点开始,标记可达实例。此步骤和用户线程同时执行。
  • 重新标记:重新标记是使用多线程对修改的引用,重新标记。此步骤需要讲用户线程暂停,但由于是对修改过的引用重新标记,所以停顿时间较短。
  • 并发清除:多线程并发清除不可达实例。无需暂停用户线程。

重新标记时是如何知道哪些引用被修改过呢?

JVM在管理自身的虚拟内存时使用和操作系统相同的分页管理模式,在JVM中称其为卡片,JVM使用了卡表记录每一个卡片的初始引用,类似于操作系统的页表。当一个实例修改时,会触发JVM的写屏障,会将修改的目标卡片在页表中标记为脏。比如A引用B改为了A引用C,那么A所在的卡片就会标记为脏。在重新标记时就会顺着A再次查找,标记C为可达引用,防止被垃圾回收器清除

4. G1 GC(Garbage First)

相较于其他的垃圾回收器,G1的机制相对来说比较复杂。其并不在简单的区分堆内存为新生代还是老年代,而是将整个堆内存分为若干份,每一份都可能是老年代,Eden区或S区。G1针对这些区域垃圾回收分为三种类型,分别是新生代垃圾回收,混合垃圾回收,全局回收。

1. 新生代垃圾回收

触发时机

当存在新生代Eden区占用内存为满时,则会触发新生代回收。

原理

新生代回收只会对Eden区以及S区实例进行回收,整个过程为复制算法,将E区复制到S区,删除E区,S区到达一定次数后转移至老年区,但和其他时候不同的时,转移到老年区的实例会被立即标记为可达,后续会说原因

3. 混合垃圾回收

触发时机

混合垃圾回收的触发条件时老年代空间使用率大于一半时触发。

原理

混合垃圾回收会将新生代和老年代一起进行回收,对于新生代则是和新生代垃圾回收一致,全部进行扫描回收。而对于老年代,则会对扫描结果进行排序,清理垃圾比例靠前的若干老年代。

垃圾混合回收的扫描和清除阶段也是分开的,触发时机不同。老年代内存利用率为一半时只会触发清理,只有当整个堆内存利用率为一半时,才会触发标记扫描,而老年代清除则是不断复用上一次的扫描结果,这也是为什么转移到老年代的实例会被立即标记为可达的原因,方式新的老年代实例被误删。整个标记过程和CMS基本一致。

3. 全局清除

触发时机

全局清除的触发时机有多种可能,如堆内存耗尽,混合垃圾回收并没有将老年代内存利用率控制住,老年代内存爆满等等,总结来说就是当堆内存已经快要饱满,可能无法正常工作的极端情况才会使用全局清除,全局清除会造成较长时间的停顿,JVM会尽量避免使用全局清除。

以上四种GC是在Java8中可以使用的,其中G1在Java8中为实验性GC,除此之外还有ZGC(Z Garbage Collector)和Shenandoah GC其在Java11中才可以使用。这里暂时先不说了。

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

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

相关文章

【拥抱AI】如何查看Milvus的使用情况?

查看Milvus的使用情况和性能指标可以帮助你了解数据库的健康状况、性能指标和资源使用情况。以下是一些常用的方法和工具,帮助你全面监控和查看Milvus的使用情况和性能指标。 1. 查看日志 Milvus的日志文件记录了运行时的各种信息,包括错误、警告和调…

基于Netty实现聊天室

前言 了解了Netty的基本功能和相关概念,使用基于Netty实现多人聊天的功能。 需求 1.服务端能够接收客户端的注册,并且接受用户的信息注册 2.服务端能够处理客户端发送的消息,并且根据消息类型进行私发或者广播发送消 3.服务端能够私发消…

利用 Jsoup 进行高效 Web 抓取与 HTML 处理

Jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 JQuery 的操作方法来取出和操作数据。 官网:https://jsoup.org/ 中文文档:Jsou…

【c语言】文件操作详解 - 从打开到关闭

文章目录 1. 为什么使用文件?2. 什么是文件?3. 如何标识文件?4. 二进制文件和文本文件?5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…

004 逻辑变量与运算

当0和1表示逻辑状态时,两个二进制数码按照某种特定的因果关系进行的运算——就叫:逻辑运算 1.二值逻辑变量与基本逻辑运算 逻辑代数: 与普通代数不同,逻辑代数中的变量只有0和1两个可取值,它们分别用来表示完全两个对立的逻辑状态 逻辑运…

Deepnote、JupyterLab、Google Colab、Amazon SageMaker、VS Code对比

功能比较 平台语言支持扩展性数据连接可视化能力DeepnotePython、R、SQL中等,依赖云端支持主要云平台(BigQuery、Snowflake等)内置仪表盘与交互图表JupyterLab多种语言,插件支持广泛极高,完全可自定义使用库&#xff…

网络安全中的数据科学如何重新定义安全实践?

组织每天处理大量数据,这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难,常常导致疏忽。以前,公司依靠 FUD 方法(恐惧、不确定性和怀疑)来识别潜在攻击。然而,将数据科学集成到网络安全中…

C语言数据结构与算法--简单实现队列的入队和出队

(一)队列的基本概念 和栈相反,队列(Queue)是一种先进先出(First In First Out)的线性表。只 允许在表的一端进行插入,而在另一端删除元素,如日常生活中的排队现象。队列中 允许插入的一端叫队尾…

快速理解微服务中Sentinel怎么实现限流

Sentinel是通过动态管理限流规则,根据定义的规则对请求进行限流控制。 一.实现步骤 1.定义资源:在Sentinel中,资源可以是URL、方法等,用于标识需要进行限流的请求;(在Sentinel中,需要我们去告诉Sentinel哪些…

matlab根据excel表头筛选表格数据

有如下表格需要筛选: 如果要筛选style中的A,color中的F2,num中的3。 代码如下: clear;clc; file_Pathstrcat(F:\csdn\,test1.xlsx); %表格路径、文件名 E1readtable(file_Path,Sheet,1); %读取表格中的字母和数字,1代表第一个…

学习日志016--python实现双向循环列表与链栈

python中一些复合数据结构通过类的封装来实现的。双向循环链表与链栈也在其中。 双向循环链表 双向循环链表是一种特殊类型的链表,它结合了双向链表和循环链表的特点。在双向循环链表中,每个节点不仅包含数据,还持有指向前一个和后一个节点的…

【Docker】常用命令汇总

Docker 是1个开源的应用容器引擎,基于Go 语言并遵从 Apache2.0 协议开源。 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙箱机制,相…

QT QRadioButton控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…

Docker部署mysql:8.0.31+dbsyncer

Docker部署mysql8.0.31 创建本地mysql配置文件 mkdir -p /opt/mysql/log mkdir -p /opt/mysql/data mkdir -p /opt/mysql/conf cd /opt/mysql/conf touch my.config [mysql] #设置mysql客户端默认字符集 default-character-setUTF8MB4 [mysqld] #设置3306端口 port33…

[SUCTF 2019]EasySQL--详细解析

信息搜集 进入界面是一个搜索框: 查看一下源代码,显示是POST传参: 随便上传个数字1: 抓包测试一下闭合,发现以双引号闭合会回显nonono,单引号闭合则无回显。 由于没有报错信息,所以我们不能确定具体的闭…

警钟长鸣,防微杜渐,遨游防爆手机如何护航安全生产?

近年来,携非防爆手机进入危险作业区引发爆炸的新闻屡见报端。2019年山西某化工公司火灾,2018年延安某煤业瓦斯爆炸,均因工人未用防爆手机产生静电打火引发。涉爆行业领域企业量大面广,相当一部分企业作业场所人员密集,…

【智能流体力学】RAG大模型方法:解决固体力学和流体动力学问题

【使用 AutoGen + GPT-4o + Chainlit UI 进行工程仿真的对话式多智能体 AI 聊天机器人】 本项目构建了一个由多个AI代理组成的系统,这些代理通过使用Microsoft AutoGen进行对话交互,能够自主地创建和仿真固体力学(FEA)和流体动力学(CFD)问题。每个AI代理都擅长规划、问题…

Redis与MySQL如何保证数据一致性

Redis与MySQL如何保证数据一致性 简单来说 该场景主要发生在读写并发进行时,才会发生数据不一致。 主要流程就是要么先操作缓存,要么先操作Redis,操作也分修改和删除。 一般修改要执行一系列业务代码,所以一般直接删除成本较低…

Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导 一、前言 在充满活力与激情的校园生活中,校运会不仅是…

【西瓜书】神经网络-MP神经元、感知机和多层网络

神经网络(neural networks)的定义:神经网络是由具有适应性的简单单元组成的广泛并行互联的网络,它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。(T. Kohonen 1988年在Neural Networks创刊号上给出的定义…