JVM详解:垃圾回收机制

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中才可以使用。这里暂时先不说了。

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

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

相关文章

Kali2024.4切换xfce主题输入法失效问题

目前感觉linux下比较好用的输入法就是google pinyin,常规的安装 sudo apt install fcitx im-configsudo apt install fcitx-googlepinyin然而在切换主题后,输入法不好用了,手动添加输入法也是找不到的状态,进入排查步骤&#xff…

C++初阶学习第十三弹——容器适配器和优先级队列的概念

目录 一.容器适配器 1.2deque的原理介绍 1.3deque与vector和list的比较,以及deque的缺陷 1.4为什么选择deque作为stack和queue的底层默认容器? stack的模拟实现 queue的模拟实现 二.优先级队列 2.1priority_queue的使用 三、总结 一.容器适配器 s…

C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)

目录 题目: 无重复字符的最长子串 1. 题目解析 2. 算法原理 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口(同向双指针) 3. 代码实现 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口 题目: 无重复字符的最长子串 1. 题目解析 题目截图: 此题所说的…

easyui combobox 只能选择第一个问题解决

easyui combobox 只能选择第一个问题解决 问题现象 在拆分开票的时候,弹出框上面有一个下拉框用于选择需要新增的明细行,但是每次只能选择到第一个 选择第二条数据的时候默认选择到第一个了 代码如下 /*新增发票编辑窗口*/function addTicketDialog…

机器学习周志华学习笔记-第6章<支持向量机>

机器学习周志华学习笔记-第6章<支持向量机> 卷王&#xff0c;请看目录 6支持向量机6.1 函数间隔与几何间隔6.1.1 函数间隔6.1.2 几何间隔 6.2 最大间隔与支持向量6.3 对偶问题6.4 核函数6.5 软间隔支持向量机6.6 支持向量机6.7核方法 6支持向量机 支持向量机是一种经典…

111. UE5 GAS RPG 实现角色技能和场景状态保存到存档

实现角色的技能存档保存和加载 首先&#xff0c;我们在LoadScreenSaveGame.h文件里&#xff0c;增加一个结构体&#xff0c;用于存储技能相关的所有信息 //存储技能的相关信息结构体 USTRUCT(BlueprintType) struct FSavedAbility {GENERATED_BODY()//需要存储的技能UPROPERT…

ArcGIS pro中的回归分析浅析(加更)关于广义线性回归工具的补充内容

在回归分析浅析中篇的文章中&#xff0c; 有人问了一个问题&#xff1a; 案例里的calls数据貌似离散&#xff0c;更符合泊松模型&#xff0c;为啥不采用泊松而采用高斯呢&#xff1f; 确实&#xff0c;在中篇中写道&#xff1a; 在这个例子中我们为了更好地解释变量&#x…

从 HTML 到 CSS:开启网页样式之旅(二)—— 深入探索 CSS 选择器的奥秘

从 HTML 到 CSS&#xff1a;开启网页样式之旅&#xff08;二&#xff09;—— 深入探索 CSS 选择器的奥秘 前言一、CSS基本选择器1. 通配选择器2. 元素选择器3. 类选择器4. id选择器5.基本选择器总结 二、CSS复合选择器1. 后代选择器2. 子选择器3. 相邻兄弟选择器4.交集选择器5…

【机器学习chp7】SVM

参考1&#xff0c;笔记 SVM笔记.pdf 参考2&#xff1a;王木头视频 什么是SVM&#xff0c;如何理解软间隔&#xff1f;什么是合叶损失函数、铰链损失函数&#xff1f;SVM与感知机横向对比&#xff0c;挖掘机器学习本质_哔哩哔哩_bilibili 目录 一、SVM模型 二、构建决策函…

【C++】读取数量不定的输入数据

读取数量不定的输入数据 似乎是一个很实用的东西&#xff1f; 问题&#xff1a; 我们如何对用户输入的一组数&#xff08;事先不知道具体有多少个数&#xff09;求和&#xff1f; 这需要不断读取数据直至没有新的输入为止。&#xff08;所以我们的代码就是这样设计的&#x…

HarmonyOS4+NEXT星河版入门与项目实战(20)------状态管理@ObjectLink @Observed

文章目录 1、用法图解2、案例实现1、任务类改造2、参数改造变量3、完整代码4、运行效果4、总结1、用法图解 2、案例实现 上一节的案例中,一直有一个功能没有生效,就是任务完成后对应的任务行变灰,任务字体出现中划线删除的效果。而该功能一直不生效的原因就是要改变的数据值…

2024年工信部大数据分析师证书报考条件是怎样的?有什么用

大数据分析师&#xff0c;乃是这样一类专业人才&#xff0c;他们凭借着先进且高效的数据分析技术以及各类实用工具&#xff0c;对规模庞大、纷繁复杂的海量数据展开全面而细致的清洗、处理、分析以及解读工作。其工作的核心目标在于为企业的决策制定提供有力依据&#xff0c;推…

基于vite创建的react18项目的单元测试

题外话 最近一个小伙伴进了字节外包&#xff0c;第一个活就是让他写一个单元测试。 嗯&#xff0c;说实话&#xff0c;在今天之前我只知道一些理论&#xff0c;但是并没有实操过&#xff0c;于是我就试验了一下。 通过查询资料&#xff0c;大拿们基本都说基于vite的项目&…

探秘嵌入式位运算:基础与高级技巧

目录 一、位运算基础知识 1.1. 位运算符 1.1.1. 与运算&#xff08;&&#xff09; 1.1.2. 或运算&#xff08;|&#xff09; 1.1.3. 异或运算&#xff08;^&#xff09; 1.1.4. 取反运算&#xff08;~&#xff09; 1.1.5. 双重按位取反运算符&#xff08;~~&#xf…

SpringBoot - 优雅的实现【账号登录错误次数的限制和锁定】

文章目录 Pre需求实现步骤简易实现1. 添加依赖2. 配置文件3. 自定义注解4. AOP切面5. 使用自定义注解&#xff1a;6. 测试 附总结 Pre SpringBoot - 优雅的实现【流控】 需求 需求描述&#xff1a; 登录错误次数限制&#xff1a;在用户登录时&#xff0c;记录每个账号的登录错…

SRIO DRP动态速率配置说明(详细讲解)

目录 一、SRIO IP时钟结构 1、时钟内部结构 2、时钟直接的关系 3、时钟计算原理 ​二、SRIO DRP介绍 ​1、MMCM DRP配置(xapp888) 2、CPLL DRP配置(ug476) 关于CPLL DRP配置详细介绍&#xff1a; GTX中CPLL、QPLL DRP动态配置方法&#xff08;详解&#xff09;-CSDN博客…

动态规划之背包问题

0/1背包问题 1.二维数组解法 题目描述&#xff1a;有一个容量为m的背包&#xff0c;还有n个物品&#xff0c;他们的重量分别为w1、w2、w3.....wn&#xff0c;他们的价值分别为v1、v2、v3......vn。每个物品只能使用一次&#xff0c;求可以放进背包物品的最大价值。 输入样例…

推荐一款龙迅HDMI2.0转LVDS芯片 LT6211UX LT6211UXC

龙迅的HDMI2.0转LVDS芯片LT6211UX和LT6211UXC是两款高性能的转换器芯片&#xff0c;它们在功能和应用上有所差异&#xff0c;同时也存在一些共同点。以下是对这两款芯片的详细比较和分析&#xff1a; 一、LT6211UX 主要特性&#xff1a; HDMI2.0至LVDS和MIPI转换器。HDMI2.0输…

深度学习模型:循环神经网络(RNN)

一、引言 在深度学习的浩瀚海洋里&#xff0c;循环神经网络&#xff08;RNN&#xff09;宛如一颗独特的明珠&#xff0c;专门用于剖析序列数据&#xff0c;如文本、语音、时间序列等。无论是预测股票走势&#xff0c;还是理解自然语言&#xff0c;RNN 都发挥着举足轻重的作用。…

[STM32]从零开始的STM32 FreeRTOS移植教程

一、前言 如果能看到这个教程的话&#xff0c;说明大家已经学习嵌入式有一段时间了。还记得嵌入式在大多数时候指的是什么吗&#xff1f;是的&#xff0c;我们所说的学习嵌入式大部分时候都是在学习嵌入式操作系统。从简单的一些任务状态机再到复杂一些的RTOS&#xff0c;再到最…