一、对象内存回收

对于对象回收,需要先判断垃圾对象,然后收集垃圾。

收集垃圾采用垃圾收集算法和垃圾收集器。

判断垃圾对象,通常采用可达性分析算法。

引用计数法

每个对象设置一个引用计数器。每被引用一次,计数器就加1,取消引用,计数器就减1,任何时候计数器为0的对象就不可能在被使用了,可被回收。

实现简单、效率高,但不能解决循环引用的问题,A引用B、B引用A,但不被其他对象引用,计数器都为1,虽不会在被使用,但不会被回收。

可达性分析算法

从GC Root起搜索引用的对象,并进行标记,被标记的对象即为存活对象,其他未被标记的对象为垃圾对象。

GC Root对象:线程栈的本地对象、静态变量、本地方法栈的变量等。

二、垃圾收集算法

分代收集理论

当代虚拟机的垃圾收集都采用分代收集算法,根据对象存活周期的不同将内存分新生代、老年代,根据各个年代的特点使用合适的垃圾收集算法。

理论依据:99%的对象都是朝生夕死

新生代:每次收集会回收大量对象,所以选择复制算法,复制少量的对象即可完成垃圾收集(其余对象直接清理掉)。

老年代:对象存活几率高,同时没有额外的空间进行分配担保,所以选择标记-清除算法标记-整理算法(比复制算法慢10倍以上)。

1、标记-复制算法

将内存分为相同的两块,每次使用一块。垃圾回收时将存活的对象复制到另一块,并清理掉该块全部的内容。

**优点:**回收效率高,每次只需要复制极少一部分对象,清除对象的时候直接情况当前内存块的全部内容;

**缺点:**只能在两块内存空间中的一块上进行分配内存。

结合以上优缺点,在hotspot的实现中,设计了eden:s1:s2=8:1:1,即利用了回收效率高的优点,又一定程度上避免了内存使用率低的问题(仅有十分之1的内存块不能同时使用)。

3、标记-清除算法

算法分为标记清除阶段

两种实现方式:

  • 标记存活的对象,回收其他所有未被标记的对象(一般采用这种);
  • 标记出垃圾对象,标记完成后统一回收所有被标记的对象。

缺点:

  • 空间碎片问题,标记清除后会产生大量不连续的碎片;
  • 标记效率不高,如果需要标记的对象太多,效率会低。
4、标记-整理算法

算法分为标记和移动两个阶段,标记阶段同标记-清除算法,标记之后存活的对象移动到内存空间的一端,然后清理掉端边界以外的内存。

最终使对象连续存储,空闲空间在另一侧,不在有内存碎片。

三、垃圾收集器

  • 垃圾收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现。
  • 没有最好和通用的垃圾收集器,结合垃圾收集器的特点,根据应用场景选择合适的垃圾收集器。

在这里插入图片描述

1、Serial串行垃圾收集器
  • 年轻代使用Serial,jvm开启参数:-XX:+UseSerialGC,采用复制算法

  • 老年代使用Serial Old,jvm开启参数:-XX:+UseSerialOldGC,采用标记-整理算法

使用单线程执行垃圾收集工作,进行垃圾收集工作时暂停所有的工作线程

**优点:**简单、没有线程交互的开销

**缺点:**比较多线程的垃圾收集器,回收效率自然就慢

在这里插入图片描述

由图可知:始终仅有一个GC线程,同时不会贺应用程序线程并行。

2、Parallel Scavenge收集器

Parallel垃圾收集器实际就是Serial收集器的多线程版本,相比Serial垃圾收集器使用了多线程进行垃圾收集,其余类似。

是JDK8默认的新生代贺老年代收集器。

默认的收集线程与CPU核数相同,可通过参数-XX:ParallelGCThreads指定收集线程数,但一般不建议修改。

  • 年轻代使用Parallel,JVM开启参数:-XX:+UseParallelGC,采用复制算法

  • 老年代使用Parallel Old收集器,JVM开启参数:-XX:+UseParallelOldGC,采用标记-整理算法

优点:Parallel Scavenge收集器关注的是吞吐量(高效率利用CPU)

​ 提供了很多参数供用户找到最合适的停顿时间或最大的吞吐量(可将内存管理优化由JVM自行完成)。

在这里插入图片描述

由图可知:Parallel收集器GC线程有多个,可并发进行垃圾收集,但不会应用程序线程并行运行。

3、ParNew收集器

用于新生代的垃圾收集器,采用复制算法,开启参数:-XX:+UseParNewGC

跟Parallel收集器类似,区别主要是ParNew可配合CMS使用。

只有它能与CMS收集器配合工作。

在这里插入图片描述

4、CMS收集器

Concurrent Mark Sweep:并发 标记 清除

用于老年代的垃圾收集器,采用标记-清除算法,JVM开启参数:-XX:+UseConcMarkSweepGC

优点:CMS收集器关注获取最短回收停顿时间,注重用户体验。

​ 真正意义的并发收集器,第一次实现了垃圾收集线程与用户线程同时工作。

工作过程

  1. 初始标记:暂停工作线程,只标记由GC Root直接引用的对象,速度很快。
  2. 并发标记:与工作线程并发运行,从GC Root直接引用的对象开始遍历整个对象图。
    • 耗时较长但不需要停顿用户工作线程;
    • 由于用户线程继续运行,可能会导致已标记的对象状态发生改变;
  3. 重新标记:暂停工作线程,做重新标记,修正并发标记期间用户线程运行导致标记产生变动的一部分对象,主要处理漏标问题。用三色标记法的增量更新算法
    • 漏标:说明没有把需要存活的对象标记为存活状态,如果执行清理,程序会崩溃;
    • 多标:本可以清理的对象标记为了存活**(浮动垃圾)**,不影响系统运行,最多占点空间,可在下一次垃圾收集过程中清理掉。
  4. 并发清理:与工作线程并发运行,GC线程对未标记为存活的对象做清理。
    • 对于该过程新增的对象标记为黑色,本过程不做任何处理;
  5. 并发重置:与工作线程并发运行,重置本次GC过程中标记的数据。
    在这里插入图片描述

由图可知:

​ CMS一次垃圾收集由5个步骤,分别是:初始标记、并发标记、重新标记、并发清理、并发重置

​ 其中初始标记和重新标记将暂停工作线程,不与工作线程并发运行

优点:并发收集、停顿低,用户体验好;

缺点

  • 会与工作线程抢占CPU资源;

  • 无法处理浮动垃圾,因为在并发标记和并发清理过程中可能会产生垃圾,只能等到下一次GC清理;

  • 回收算法“标记-清理算法”会产生大量内存碎片

    • 可通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理
  • 会出现"concurrent mode failure",此时CMS会退化成serial old垃圾收集器。

    • 在并发标记和并发清理过程中,由于工作线程在同时运行,可能又导致内存不足触发full GC

CMS的相关核心参数

  1. -XX:+UseConcMarkSweepGC:启用cms

  2. -XX:ConcGCThreads:并发的GC线程数

  3. -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)

  4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次

  5. -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比):尽早执行垃圾收集,降低出现"concurrent mode failure"发生的可能。

  6. -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整。

  7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,降低CMS GC标记阶段**(也会对年轻代一起做标记,如果在minor gc就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)**时的开销,一般CMS的GC耗时 80%都在标记阶段。

  8. -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW。

  9. -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW。

四、优化

很多优化无非就是让短期存活的对象尽量都留在survivor里,不要进入老年代,这样在minor gc的时候这些对象都会被回收,不会进到老年代从而导致full gc

只要年轻代参数设置合理,老年代CMS的参数设置基本都可以用默认值。

五、垃圾收集底层算法实现-三色标记

在并发标记过程中,因为期间应用线程还在继续跑,对象的引用可能发生变化,会存在多标和漏标的情况。

**多标:**把本该是垃圾的对象标记为存活,产生浮动垃圾。

  • 产生情况:在并发标记过程中,由于方法结束导致部分局部变量(GC Root)被销毁,这个GC Root之前又被标记为非垃圾对象,那么本轮GC不会回收这部分对象。
  • 不会影响垃圾回收的正确性,只需要等到下一轮垃圾回收。
  • 解决办法:当前不用解决,可由下一次进行收集。

**漏标:**把本该存活的对象没有标记为存活,导致本该存活的对象被删除,导致严重bug,必须解决。解决办法:主要引入了三色标记算法解决。

🔺三色标记算法:

三色标记算法把GC Roots可达性分析遍历对象过程中遇到的对象,按照

“是否访问过”标记为三种颜色:

  • **黑色:**表示对象已经被垃圾收集器访问过,且这个对象的所有引用的对象都已经扫描。
    • 黑色的对象代表已经访问过,且是安全存活的。
    • 如果其他对象引用指向了黑色对象,无须重新扫描一遍。
    • 黑色的对象不会不经过灰色对象直接引用白色对象。
  • **灰色:**表示对象已经被垃圾收集器访问过,但这个对象的所引用的对象至少还有一个未被扫描。
  • **白色:**表示对象尚未被垃圾收集器访问过。
    • 在可达性分析刚开始阶段所有对象均为白色标记。
    • 若可达性分析结束对象仍为白色则表示对象不可达。
处理漏标的两种方案

增量更新**(Incremental Update)和原始快照(Snapshot At The Beginning,SATB)**

增量更新:当黑色对象新增加了指向白色对象的引用关系时,将新插入的引用记录下来。等并发扫描结束之后将记录的引用以黑色对象为根重新扫描一次**(重新标记)**。

  • 因为黑色对象一旦插入了引用白色对象,就不能在是黑色了,该变成白色对象了,所以要记录并重新标记。
  • 为了使在并发标记过程中新加入的白色对象也被扫描到,否则会被误认为是非存活对象而被删除。

原始快照:当**删除灰色对象指向的白色对象的引用**时,将删除的引用记录下来。在并发扫描结束之后以灰色对象为根,重新扫描一次,这样便能扫描到白色对象。

  • 直接将白色对象标记为黑色。(该对象可能确实可以回收,而产生浮动垃圾)
  • 如果指向该白色对象的来自灰色对象的引用全部被删除,则该白色对象就不能被扫描到,但如果新增了来自黑色对象的引用,则该白色对象不该被删除。
    • 因为没有判断是否新增了来自黑色对象的引用,所以直接标记为黑色对象,让其在下一次GC时扫描判断。
读写屏障

虚拟机对新增引用关系和删除引用关系的记录,都是通过写屏障实现的。

写屏障:就是在赋值操作前后加入一些处理,把引用记录下来。

读屏障:当读取成员变量时,记录读取到的对象。

在HotSpot虚拟机中,并发标记时处理漏标的方案:

  • CMS:写屏障+增量更新
  • G1:写屏障+SATB

其他功能:

  1. 写屏障可用于记录跨代/区引用的变化;
  2. 读屏障可用于支持移动对象的并发执行
思考:并发标记处理漏标的方案中,为什么G1用SATB,CMS用增量更新?
SATB相对增量更新效率更高。
原始快照在重新标记阶段直接把记录的白色对象标记为黑色,不需要再次深度扫描背删除对应对象;增量更新会记录的黑色对象为根重新扫描一次。
而,CMS的老年代区域为一块内存,G1的对象分布在不同的region中,G1重新深度扫描代价会高于CMS。
所以G1选择SATB不做深度扫描,只是简单标记,浮动垃圾对象交给下一次GC。
记忆集和卡表

在新生代的垃圾收集进行GC可达性分析时,需要知道对象有没有被老年代的对象引用。

为了保证垃圾收集的效率,不能把所有老年代的对象扫描一遍,为此引用了**记忆集(Remember Set)**的数据结构,记录非收集区对象指向收集区对象的引用,避免直接扫描老年代。

在垃圾收集的扫描标记过程中,只需要通过记忆集判断对象是否有被非收集区对象引用,如果有标记对象不可回收。

此外,不只是新生代和老年代之间有跨代引用的问题,所有部分收集的垃圾收集器,如G1,都会涉及跨区引用的问题。

🔺hotspot虚拟机使用**卡表(cardtable)**的方式实现记忆集:

卡表使用一个字节数组实现,CARD_TABLE[],每个元素对应着其标识的内存区域一块特定大小的内存块,称为”卡页“。

一个卡页中可包含多个对象,只要有一个对象的字段存在跨代指针,其对应的卡表元素标识为1,表示该元素变脏,否则为0,GC时只要筛选本收集区的卡表中变脏的元素加入到GC ROOTs中。(不明白,卡页对应一批对象?一个为脏,全页为脏?)

Hotspot中使用写屏障维护卡表状态。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于python网络数据挖掘的二手房推荐系统

基于网络数据挖掘的二手房推荐系统设计与实现 【摘要】 随着互联网技术在房地产行业的深入应用,线上房源信息呈爆炸式增长,给购房者带来了信息过载的挑战。为了提升二手房筛选的效率与精准度,本文设计并实现了一个基于网络数据挖掘的二手房推…

Java + 阿里云 Gmsse 实现 SSL 国密通信

前言 解决接口或页面仅密信浏览器(或 360 国密浏览器)能访问的问题 测试页面 测试网站-中国银行:https://ebssec.boc.cn/boc15/help.html 使用其他浏览器(google,edge等)打开 使用密信浏览器打开 解决…

国产数据库分类总结

文章目录 一、华为系数据库1. 华为 GaussDB 二、阿里系数据库1. 阿里云 OceanBase2. PolarDB(阿里云自研) 三、腾讯系数据库1. TDSQL(腾讯云)2. TBase(PostgreSQL增强版) 四、传统国产数据库1. 达梦数据库&…

解密闭包:函数如何记住外部变量

🧠 什么是闭包? 闭包是一个函数对象,它不仅记住它的代码逻辑,还记住了定义它时的自由变量(即非全局也非局部,但被内部函数引用的变量)。即使外部函数已经执行完毕,这些自由变量的值…

I2C协议详解及STM32 HAL库硬件I2C卡死问题分析

一、I2C协议详解 1. I2C协议概述 Inter-Integrated Circuit (I2C) 是由 Philips 半导体(现 NXP 半导体)于 1980 年代设计的一种同步串行通信总线协议。该协议采用半双工通信模式,支持多主从架构,专为短距离、低速率的芯片间通信…

HTTP协议-后端接收请求

起因就是不知道post这个请求体中这些格式有什么区别,后端又怎么去接收这些不同格式的内容 Get请求 get请求是比较简单的一类 正常的直接用参数接收(不写的话名字要匹配)或者RequestParam都可以接收,用对象绑定也可以 resultful…

HTML5 实现的圣诞主题网站源码,使用了 HTML5 和 CSS3 技术,界面美观、节日氛围浓厚。

以下是一个 HTML5 实现的圣诞主题网站源码,使用了 HTML5 和 CSS3 技术,界面美观、节日氛围浓厚。它包括: 圣诞树动画 🎄雪花飘落特效 ❄️圣诞祝福语 🎁响应式布局,适配移动端 你可以将代码保存为 index.…

Spring Cloud Bus 和 Spring Cloud Stream

Spring Cloud Bus 和 Spring Cloud Stream 都是 Spring Cloud 生态中的消息通信组件,但它们的定位和使用场景有显著区别: 1. Spring Cloud Bus 核心定位:分布式系统的消息广播(配置刷新、事件传播)。 典型场景&#x…

磁悬浮轴承位移信号的高精度估计:卡尔曼滤波算法深度解析

无需位移传感器,滤波算法如何实现微米级精度? 磁悬浮轴承作为革命性的非接触式支承技术,凭借无磨损、无需润滑、高转速等优势,在飞轮储能、高速电机、人工心脏泵和航空航天领域获得了广泛应用。其核心控制依赖于对转子位移信号的高精度实时检测,传统电涡流传感器虽能提供位…

DAY 43 预训练模型

目录 一、预训练的概念 二、 经典的预训练模型 2.1 CNN架构预训练模型 2.2 Transformer类预训练模型 2.3 自监督预训练模型 三、常见的分类预训练模型介绍 3.1 预训练模型的发展史 3.2 预训练模型的训练策略 知识点回顾: 预训练的概念常见的分类预训练模型图像…

Redis:事物

🌈 个人主页:Zfox_ 🔥 系列专栏:Redis 🔥 什么是事务 Redis的事务和MySQL的事务概念上是类似的.都是把⼀系列操作绑定成⼀组.让这⼀组能够批量执⾏. 但是注意体会Redis的事务和MySQL事务的区别: 弱化的原⼦性:redi…

CppCon 2018 学习:An allocator is a handle to a heap Lessons learned from std::pmr

“An allocator is a handle to a heap — Lessons learned from std::pmr” 翻译过来就是:“分配器(allocator)是对堆(heap)的一种句柄(handle)——从 std::pmr 中学到的经验”。 基础概念 分…

设备健康实时监测方法演进:从传感网络到AI决策树的工业智能实践

引言:当设备运维遇上AIoT革命 在工业4.0进程中,​毫秒级设备状态捕获能力正成为智能工厂的核心竞争力。传统监测方法因数据滞后、诊断粗放被诟病,本文将深入探讨三大前沿实时监测技术路径,并揭秘中讯烛龙系统如何通过深度强化学习…

剑指offer53_二叉树的深度

二叉树的深度 输入一棵二叉树的根结点,求该树的深度。 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 数据范围 树中节点数量 [ 0 , 500 ] [0,500] [0,500]。 样例 输入&#…

探秘AI的秘密:leaked-system-prompts

揭秘:揭秘系统提示合集背后的秘密 在当今这个人工智能技术迅速发展的时代,了解和使用大型语言模型(LLM)已成为技术爱好者、开发者和研究人员的共同目标。而作为核心组成部分,系统提示(system prompts)的设计和应用直接影响了LLM的表现和功能。今天, 我们将为大家揭示一…

Gaming Mode四大功能(VRR、QMS、QFT、ALLM)

HDMI 2.1定义的Gaming Mode四大功能(VRR、QMS、QFT、ALLM)通过协同优化帧传输、刷新率同步与延迟控制,显著提升了游戏和影音的流畅性与响应速度。以下是这些功能的详细解析及其应用价值: 🔄 1. 可变刷新率(…

数据库总结(关系代数-函数依赖-范式)

以下是关系代数中基本操作的详细说明: 并(Union) 关系R和S的并操作表示为R ∪ S,要求R和S具有相同的属性集(并相容性)。结果包含所有属于R或S的元组,自动去除重复项。 示例: R …

react经验:在nextjs中使用motion组件

什么是motion组件? 一种动画组件 motion组件文档 在nextjs中的应用步骤 1.安装motion npm i framer-motion2.在next.config.js中配置转义 export default {transpilePackages: [framer-motion] }3.开始应用 **注意要点:**在服务端渲染不可直接用&am…

怎样大语言模型 遵守规则

如何让应用中的提示工程更能适应未来变化 目录 如何让应用中的提示工程更能适应未来变化怎样大语言模型 遵守规则提示词 很有效:Memorize these rules提示可分为稳定组件和易变组件怎样大语言模型 遵守规则 实验背景:让大语言模型可靠地遵守规则很难,尤其是规则数量增多时。…

如何通过SSL证书配置防止源站IP泄露 - 全面防护指南

问题背景:SSL证书如何导致源站IP泄露 近期多位站长反馈,即使已部署高防CDN并做好源站IP保密工作,服务器仍频繁遭受DDoS攻击。经深入排查,发现问题根源在于SSL证书。当前网络环境中存在大量爬虫工具24小时不间断扫描全网IP地址&am…